How to use AccelStepper library to create movement functions?

Hi all,

I was wondering if anyone has any experience with using the following library for stepper motor control. I can make the motors turn forever, however I am having a incredibly hard time trying to make specific functions to spin the motors clockwise, anticlockwise, and independently of eachother as a function call.

Here's what I've tried (Didn't work):

// MultiStepper
// -*- mode: C++ -*-
//
// Control both Stepper motors at the same time with different speeds
// and accelerations. 
// Requires the AFMotor library (https://github.com/adafruit/Adafruit-Motor-Shield-library)
// And AccelStepper with AFMotor support (https://github.com/adafruit/AccelStepper)
// Public domain!

#include <AccelStepper.h>
#include <AFMotor.h>

// two stepper motors one on each port
AF_Stepper motor1(200, 1);
AF_Stepper motor2(200, 2);

// you can change these to DOUBLE or INTERLEAVE or MICROSTEP!
// wrappers for the first motor!
void forwardstep1() {  
  motor1.onestep(FORWARD, SINGLE);
}
void backwardstep1() {  
  motor1.onestep(BACKWARD, SINGLE);
}
// wrappers for the second motor!
void forwardstep2() {  
  motor2.onestep(FORWARD, SINGLE);
}
void backwardstep2() {  
  motor2.onestep(BACKWARD, SINGLE);
}

// Motor shield has two motor ports, now we'll wrap them in an AccelStepper object
AccelStepper stepperRight(forwardstep1, backwardstep1);
AccelStepper stepperLeft(forwardstep2, backwardstep2);

void setup() {
  stepperRight.setMaxSpeed(100);
  stepperRight.setAcceleration(50.0);
  stepperLeft.setMaxSpeed(100);
  stepperLeft.setAcceleration(50.0);
}

void moveRight(int steps) // STEPS MUST BE NEG TO MAKE IT REVERSE
{
  stepperRight.moveTo(steps);
  stepperRight.setSpeed(100);
  stepperRight.run();
}

void moveLeft(int steps)
{
  stepperLeft.moveTo(steps);
  stepperLeft.setSpeed(100);
  stepperLeft.run();
}

void moveForward()
{
  stepperRight.moveTo(1000);
  stepperRight.setSpeed(100);
  stepperLeft.moveTo(1000);
  stepperLeft.setSpeed(100);
  stepperRight.run();
  stepperLeft.run();
}

void moveBackward()
{
  stepperRight.moveTo(-1000);
  stepperRight.setSpeed(100);
  stepperLeft.moveTo(-1000);
  stepperLeft.setSpeed(100);
  stepperRight.run();
  stepperLeft.run();
}

void loop() {
  int steps = random(0, 1000);
   moveForward();
   delay(1000);
   moveBackward();
   delay(1000);
   moveRight(steps);
   delay(1000);
   moveLeft(steps);
   delay(1000); 
}

This code was meant to just make the motors spin together clockwise, then together anticlockwise, then seperately a random length of time.

Has anyone got any advice? I'm well and truly stuck (been trying for 3 hours) :S

1 Like

The run() method has to be called quite often - like EVERY pass through loop(). Most times, nothing will happen. At the right time, though, the stepper will actually step.

So how would I fix this code? Run is called in every function,, surely that's enough?

Run is called in every function,, surely that's enough?

If one step per second is enough, sure.

You should have the stepper.run() calls in loop() and you should ensure that loop() repeats hundreds or thousands of times per second. When you want a motor to move just use stepper.moveTo() or stepper.move() to define the destination. When it gets there it will stop.

...R

How would I increase the frequency of the run() calls? Aren't I limited by the frequency of loop()?

pixelrain:
How would I increase the frequency of the run() calls? Aren't I limited by the frequency of loop()?

Yes. But it is normal for programs to be written so that loop() repeats very frequently. What is preventing your code from repeating quickly?

...R

Calling delay() is the main problem - nothing happens during delay.

Surely Delay here won't actually stop those motors; it's just a delay between the function MoveForward(); and the next function from running? The MoveForward() function should complete in its entirety, then wait 1 second, then go to the next function; however when testing the motors just move one step and don't move again.

pixelrain:
Surely Delay here won't actually stop those motors;

It will prevent loop() from repeating quickly. Have a look at how millis() is used to manage timing without blocking in Several things at a time

...R

Alright, well I've updated the code and managed to make some move forward and move backwards functions:

// MultiStepper
// -*- mode: C++ -*-
//
// Control both Stepper motors at the same time with different speeds
// and accelerations.
// Requires the AFMotor library (https://github.com/adafruit/Adafruit-Motor-Shield-library)
// And AccelStepper with AFMotor support (https://github.com/adafruit/AccelStepper)
// Public domain!

#include <AccelStepper.h>
#include <AFMotor.h>

// two stepper motors one on each port
AF_Stepper motor1(200, 1);
AF_Stepper motor2(200, 2);
int movement = 0;
const int MOTION = 1000;

// you can change these to DOUBLE or INTERLEAVE or MICROSTEP!
// wrappers for the first motor!
void forwardstep1() {
  motor1.onestep(FORWARD, SINGLE);
}
void backwardstep1() {
  motor1.onestep(BACKWARD, SINGLE);
}
// wrappers for the second motor!
void forwardstep2() {
  motor2.onestep(FORWARD, SINGLE);
}
void backwardstep2() {
  motor2.onestep(BACKWARD, SINGLE);
}

// Motor shield has two motor ports, now we'll wrap them in an AccelStepper object
AccelStepper stepperLeft(forwardstep1, backwardstep1);
AccelStepper stepperRight(forwardstep2, backwardstep2);

void setup() {
  stepperLeft.setMaxSpeed(200);
  stepperLeft.setAcceleration(200);
  stepperLeft.moveTo(0);
  stepperRight.setMaxSpeed(200);
  stepperRight.setAcceleration(200);
  stepperRight.moveTo(0);
}

void moveLeft(int steps) // STEPS MUST BE NEG TO MAKE IT REVERSE
{
  //stepperRight.stop();
  stepperLeft.moveTo(steps);
  stepperLeft.setSpeed(50);
  while (stepperLeft.currentPosition() != steps)
  {
    stepperLeft.run();
  }
  stepperRight.setCurrentPosition(0);
  stepperLeft.setCurrentPosition(0);

}

void moveRight(int steps)
{
  //stepperLeft.stop();
  stepperRight.moveTo(steps);
  stepperRight.setSpeed(50);
  while (stepperRight.currentPosition() != steps)
  {
    stepperRight.run();
  }
  stepperRight.setCurrentPosition(0);
  stepperLeft.setCurrentPosition(0);
}

void moveForward()
{
  stepperLeft.moveTo(MOTION);
  stepperLeft.setSpeed(50);
  stepperRight.moveTo(MOTION);
  stepperRight.setSpeed(50);

  while (stepperLeft.currentPosition() != MOTION && stepperRight.currentPosition() != MOTION)
  {
    stepperLeft.run();
    stepperRight.run();
  }

  //stepperRight.stop();
  //stepperLeft.stop();
  //stepperLeft.setCurrentPosition(stepperRight.currentPosition());
  //stepperRight.setCurrentPosition(stepperLeft.currentPosition());
  stepperRight.setCurrentPosition(0);
  stepperLeft.setCurrentPosition(0);
  movement += 1;
}

void moveBackward()
{
  stepperLeft.moveTo(-MOTION);
  stepperLeft.setSpeed(50);
  stepperRight.moveTo(-MOTION);
  stepperRight.setSpeed(50);
  while (stepperLeft.currentPosition() != -MOTION && stepperRight.currentPosition() != -MOTION)
  {
    stepperLeft.run();
    stepperRight.run();
  }

  //stepperRight.stop();
  //stepperLeft.stop();
  //stepperRight.setCurrentPosition(stepperRight.currentPosition());
  // stepperLeft.setCurrentPosition(stepperLeft.currentPosition());
  stepperRight.setCurrentPosition(0);
  stepperLeft.setCurrentPosition(0);
  movement += 1;

}

void loop() {
  int steps = random(-1000, 0);
  int steps2 = random(-1000, 0);
  if (movement == 0) {
    moveBackward();
  }
  if (movement == 1)
  {

    moveForward();
  }
  else {
    moveRight(steps);
    moveLeft(steps2);
  }
}

One issue is however the motors complete one full rotation forwards initially, then do the loop(). This happens with or without the "moveTo" in set-up; does anyone have an idea why?

Also, will use of AccelStepper block any other components onboard the Arduino? I hope to run sensors in the background which dependent on their reading will call the relevant function for the steppers; does this library remove blocking entirely, or will these sensors fail to work while the steppers run (I.e blocked)? I don't have the Arduino with me right now, but this is a very large concern!

pixelrain:
Alright, well I've updated the code and managed to make some move forward and move backwards functions:

You have not taken account of the most critical piece of advice - putting stepper.run() into loop() rather than in functions.

Study the examples that come with the Accelstepper library.

...R

I hope to run sensors in the background

You can run things "in the background" only if you have dual processors or an operating system that forces time-slicing. The Arduino has neither.

You can write non-blocking code using the AccelStepper library. Or, you can write blocking code using the AccelStepper library. You choose.

Robin2:
You have not taken account of the most critical piece of advice - putting stepper.run() into loop() rather than in functions.

Study the examples that come with the Accelstepper library.

...R

I'm uncertain as to how to integrate these functions by putting run in the loop; does the moveForward() function not get called constantly, and so I would not be able to reset the stepper motors position once they've completed their movement as it will just get reset constantly? I've read through several examples on this site (AccelStepper: Examples) but I don't see how I can get as much control by butting run() in the loop?

Edit: I rewrote it to use run in the loops. but now only one of the motors is spinning; I'm pretty sure it's blocking, but I don't know why this code is blocking it compared to the previous. What can I do to avoid blocking? This is the rewritten code:

// MultiStepper
// -*- mode: C++ -*-
//
// Control both Stepper motors at the same time with different speeds
// and accelerations.
// Requires the AFMotor library (https://github.com/adafruit/Adafruit-Motor-Shield-library)
// And AccelStepper with AFMotor support (https://github.com/adafruit/AccelStepper)
// Public domain!

#include <AccelStepper.h>
#include <AFMotor.h>

// two stepper motors one on each port
AF_Stepper motor1(200, 1);
AF_Stepper motor2(200, 2);
int movement = 0;
const int MOTION = 1000;

// you can change these to DOUBLE or INTERLEAVE or MICROSTEP!
// wrappers for the first motor!
void forwardstep1() {
  motor1.onestep(FORWARD, SINGLE);
}
void backwardstep1() {
  motor1.onestep(BACKWARD, SINGLE);
}
// wrappers for the second motor!
void forwardstep2() {
  motor2.onestep(FORWARD, SINGLE);
}
void backwardstep2() {
  motor2.onestep(BACKWARD, SINGLE);
}

// Motor shield has two motor ports, now we'll wrap them in an AccelStepper object
AccelStepper stepperLeft(forwardstep1, backwardstep1);
AccelStepper stepperRight(forwardstep2, backwardstep2);

void setup() {
  stepperLeft.setMaxSpeed(200);
  stepperLeft.setAcceleration(200);
  stepperLeft.moveTo(0);
  stepperRight.setMaxSpeed(200);
  stepperRight.setAcceleration(200);
  stepperRight.moveTo(0);
}

void moveLeft(int steps) // STEPS MUST BE NEG TO MAKE IT REVERSE
{
  //stepperRight.stop();
  stepperLeft.moveTo(steps);
  stepperLeft.setSpeed(50);
}

void moveRight(int steps)
{
  //stepperLeft.stop();
  stepperRight.moveTo(steps);
  stepperRight.setSpeed(50);
}

void moveForward()
{
  stepperLeft.moveTo(MOTION);
  stepperLeft.setSpeed(50);
  stepperRight.moveTo(MOTION);
  stepperRight.setSpeed(50);
}

void moveBackward()
{
  stepperLeft.moveTo(-MOTION);
  stepperLeft.setSpeed(50);
  stepperRight.moveTo(-MOTION);
  stepperRight.setSpeed(50);
}

void loop() {
  int steps = random(-1000, 0);
  int steps2 = random(-1000, 0);
  if (stepperLeft.distanceToGo() == 0 && stepperRight.distanceToGo() == 0)
  {
    stepperRight.setCurrentPosition(0);
    stepperLeft.setCurrentPosition(0);
    movement += 1;
  }
  
  if (movement == 0) {
    moveBackward();
  }
  if (movement == 1)
  {
    moveForward();
  }
  else {
    moveRight(steps);
    moveLeft(steps2);
  }
  stepperRight.run();
  stepperLeft.run();
}

EDIT #2: I've tried making further changes but now the motors will spin one way indefinitely; I've really messed this up :confused:

// MultiStepper
// -*- mode: C++ -*-
//
// Control both Stepper motors at the same time with different speeds
// and accelerations.
// Requires the AFMotor library (https://github.com/adafruit/Adafruit-Motor-Shield-library)
// And AccelStepper with AFMotor support (https://github.com/adafruit/AccelStepper)
// Public domain!

#include <AccelStepper.h>
#include <AFMotor.h>

// two stepper motors one on each port
AF_Stepper motor1(200, 1);
AF_Stepper motor2(200, 2);
int movement = 0;
int control = 0;
const int MOTION = 1000;

// you can change these to DOUBLE or INTERLEAVE or MICROSTEP!
// wrappers for the first motor!
void forwardstep1() {
  motor1.onestep(FORWARD, SINGLE);
}
void backwardstep1() {
  motor1.onestep(BACKWARD, SINGLE);
}
// wrappers for the second motor!
void forwardstep2() {
  motor2.onestep(FORWARD, SINGLE);
}
void backwardstep2() {
  motor2.onestep(BACKWARD, SINGLE);
}

// Motor shield has two motor ports, now we'll wrap them in an AccelStepper object
AccelStepper stepperLeft(forwardstep1, backwardstep1);
AccelStepper stepperRight(forwardstep2, backwardstep2);

void setup() {
  stepperLeft.setMaxSpeed(200);
  stepperLeft.setAcceleration(200);
  stepperLeft.moveTo(0);
  stepperRight.setMaxSpeed(200);
  stepperRight.setAcceleration(200);
  stepperRight.moveTo(0);
}

void moveLeft(int steps) // STEPS MUST BE NEG TO MAKE IT REVERSE
{
  //stepperRight.stop();
  stepperLeft.moveTo(steps);
  stepperLeft.setSpeed(50);
}

void moveRight(int steps)
{
  //stepperLeft.stop();
  stepperRight.moveTo(steps);
  stepperRight.setSpeed(50);
}

void moveForward()
{
  stepperLeft.moveTo(MOTION);
  stepperLeft.setSpeed(50);
  stepperRight.moveTo(MOTION);
  stepperRight.setSpeed(50);
  if (stepperLeft.distanceToGo() == 0 && stepperRight.distanceToGo() == 0)
  {
    stepperRight.stop();
    stepperLeft.stop();
    stepperRight.setCurrentPosition(0);
    stepperLeft.setCurrentPosition(0);
    movement += 1;
  }
}

void moveBackward()
{
  stepperLeft.moveTo(-MOTION);
  stepperLeft.setSpeed(50);
  stepperRight.moveTo(-MOTION);
  stepperRight.setSpeed(50);
  if (stepperLeft.distanceToGo() == 0 && stepperRight.distanceToGo() == 0)
  {
    stepperRight.stop();
    stepperLeft.stop();
    stepperRight.setCurrentPosition(0);
    stepperLeft.setCurrentPosition(0);
    movement += 1;
  }
}

void loop() {
  int steps = random(-1000, 0);
  int steps2 = random(-1000, 0);
  if (movement == 0 && control == 0) {
    moveBackward();
    control += 1;
  }
  if (movement == 1 && control == 1)
  {
    moveForward();
    control += 1;
  }
  else {
    moveRight(steps);
    moveLeft(steps2);
  }
  stepperRight.run();
  stepperLeft.run();
}

Edit#3: After some debugging I've found that the "moveTo" and "distanceToGo" of the motors is increasing to infinity; I assume this is happening because my code keeps adding 1000 steps every loop. How would I go about rectifying this?

Well don't do that then.

Add 1000 at the appropriate time, like after 5 seconds has passed or an external input (button) changes.

Edit#4: This is what happens after "distanceToGo" hits 0; it just explodes into negative values. Why is this happening?!

// MultiStepper
// -*- mode: C++ -*-
//
// Control both Stepper motors at the same time with different speeds
// and accelerations.
// Requires the AFMotor library (https://github.com/adafruit/Adafruit-Motor-Shield-library)
// And AccelStepper with AFMotor support (https://github.com/adafruit/AccelStepper)
// Public domain!

#include <AccelStepper.h>
#include <AFMotor.h>

// two stepper motors one on each port
AF_Stepper motor1(200, 1);
AF_Stepper motor2(200, 2);
int movement = 0;
int control = 0;
const int MOTION = 1000;

// you can change these to DOUBLE or INTERLEAVE or MICROSTEP!
// wrappers for the first motor!
void forwardstep1() {
  motor1.onestep(FORWARD, SINGLE);
}
void backwardstep1() {
  motor1.onestep(BACKWARD, SINGLE);
}
// wrappers for the second motor!
void forwardstep2() {
  motor2.onestep(FORWARD, SINGLE);
}
void backwardstep2() {
  motor2.onestep(BACKWARD, SINGLE);
}

// Motor shield has two motor ports, now we'll wrap them in an AccelStepper object
AccelStepper stepperLeft(forwardstep1, backwardstep1);
AccelStepper stepperRight(forwardstep2, backwardstep2);

void setup() {
  stepperLeft.setMaxSpeed(200);
  stepperLeft.setAcceleration(200);
  stepperLeft.moveTo(0);
  stepperRight.setMaxSpeed(200);
  stepperRight.setAcceleration(200);
  stepperRight.moveTo(0);
  Serial.begin(9600);
}

void moveLeft(int steps) // STEPS MUST BE NEG TO MAKE IT REVERSE
{
  //stepperRight.stop();
  stepperLeft.moveTo(steps);
  stepperLeft.setSpeed(50);
}

void moveRight(int steps)
{
  //stepperLeft.stop();
  stepperRight.moveTo(steps);
  stepperRight.setSpeed(50);
}

void moveForward()
{

  stepperLeft.moveTo(MOTION);
  stepperLeft.setSpeed(50);
  stepperRight.moveTo(MOTION);
  stepperRight.setSpeed(50);

}

void moveBackward()
{
  stepperLeft.moveTo(-MOTION);
  stepperLeft.setSpeed(50);
  stepperRight.moveTo(-MOTION);
  stepperRight.setSpeed(50);
}

void reset()
{
  stepperRight.stop();
  stepperLeft.stop();
  stepperRight.setCurrentPosition(0);
  stepperLeft.setCurrentPosition(0);
  movement += 1;
}

void loop() {
  int steps = random(-1000, 0);
  int steps2 = random(-1000, 0);
  Serial.print("Current Left Pos:");
  Serial.println(stepperLeft.currentPosition());
  Serial.print("Current Right Pos:");
  Serial.println(stepperRight.currentPosition());
  Serial.print("Current distance to go:");
  Serial.println(stepperLeft.distanceToGo());
  if (stepperLeft.distanceToGo() == 0 || stepperRight.distanceToGo() == 0)
  {
    reset();
  }

  if (movement == 0) {
    moveBackward();
  }

  if (movement == 1)
  {
    moveForward();
  }

  else {
    moveRight(steps);
    moveLeft(steps2);
  }

  stepperRight.run();
  stepperLeft.run();
}]/code]

This is the current code for reference. (Ran out of characters for previous post)

MorganS:
Well don't do that then.

Add 1000 at the appropriate time, like after 5 seconds has passed or an external input (button) changes.

The appropriate time should be when it's completed it's first run through of 1000; the aim of the function is for it move specifically 1000 steps each time the function is called. I tried to put an if case in the function that would force it to run the moveTo once but got the same issue; I've no idea where to go from here.

pixelrain:
I'm uncertain as to how to integrate these functions by putting run in the loop; does the moveForward() function not get called constantly,

........

Edit#3: After some debugging I've found that the "moveTo" and "distanceToGo" of the motors is increasing to infinity; I assume this is happening because my code keeps adding 1000 steps every loop. How would I go about rectifying this?

It seems to me you have yourself all in a tangle.

Put your project to one side for a few hours and take one of the Accelstepper examples and get it working to the point that you fully understand it.

I suspect that AFmotor stuff is also an unnecessary distraction.

Even if the moveForward() function is called regularly that is not the best way to use the run() function because it confuses the logic of the project. By putting the run() into loop() you can just forget about it and concentrate on how far and when the motor should move,

In another Post you wondered about negative values - that usually happens when a signed variable underflows or overflows.

...R

Robin2:
It seems to me you have yourself all in a tangle.

Put your project to one side for a few hours and take one of the Accelstepper examples and get it working to the point that you fully understand it.

I suspect that AFmotor stuff is also an unnecessary distraction.

Even if the moveForward() function is called regularly that is not the best way to use the run() function because it confuses the logic of the project. By putting the run() into loop() you can just forget about it and concentrate on how far and when the motor should move,

In another Post you wondered about negative values - that usually happens when a signed variable underflows or overflows.

...R

Alright, correct me if I'm wrong but this program should work:

//Stepper Motor Control Code
//Using AccelStepper Library


#include <AccelStepper.h> // include AccelStepper library
#include <AFMotor.h> //Using alternate AccelStepper constructor so need this

// two stepper motors one on each port
AF_Stepper motor1(200, 1);
AF_Stepper motor2(200, 2);
int movement = 0; // Used to seperate the seperate functions for testing
//Will be replaced with sensor readings
const int MOTION = 500; //Test amount of steps for craft to move to

// you can change these to DOUBLE or INTERLEAVE or MICROSTEP!
// wrappers for the first motor!
//Just defines how it will move
void forwardstep1() {
  motor1.onestep(FORWARD, SINGLE);
}
void backwardstep1() {
  motor1.onestep(BACKWARD, SINGLE);
}
// wrappers for the second motor!
void forwardstep2() {
  motor2.onestep(FORWARD, SINGLE);
}
void backwardstep2() {
  motor2.onestep(BACKWARD, SINGLE);
}

// Motor shield has two motor ports, now we'll wrap them in an AccelStepper object
AccelStepper stepperLeft(forwardstep1, backwardstep1);
AccelStepper stepperRight(forwardstep2, backwardstep2);

void setup() { // set up the two objects; default pos is 0 as per docs
  stepperLeft.setMaxSpeed(200);
  stepperLeft.setAcceleration(200);
  stepperRight.setMaxSpeed(200);
  stepperRight.setAcceleration(200);
  Serial.begin(9600);
}

void moveLeft(int steps) // Spins one stepper backwards X amount of steps
{
  //stepperRight.stop();
  stepperLeft.moveTo(-steps);
  stepperLeft.setSpeed(50); // need to set speed for this as we want
  // a constant speed for the motors; same for all subseq functions
  //moveTo changes the speed so we need to redefine it here
}

void moveRight(int steps)
{
  stepperRight.moveTo(steps);
  stepperRight.setSpeed(50); // same as above but moves right stepper
}

void moveForward()
{

  stepperLeft.moveTo(MOTION);
  stepperLeft.setSpeed(50);
  stepperRight.moveTo(MOTION);
  stepperRight.setSpeed(50);
  // this SHOULD work the same way as above; tells steppers to move
  //500 steps in the clockwise direction, as it's moveTo
  //it's an Absolute Position; meaning it is always 500 steps from the 0
  //setSpeed same use as before
}

void moveBackward()
{
  stepperLeft.moveTo(-MOTION);
  stepperLeft.setSpeed(50);
  stepperRight.moveTo(-MOTION);
  stepperRight.setSpeed(50);
  //same as above but in the counter clockwise way
}

void reset()
{
  stepperRight.stop();
  stepperLeft.stop();
  stepperRight.setCurrentPosition(0);
  stepperLeft.setCurrentPosition(0);
  movement += 1;
  //This functions resets the entire process; stops steppers
  //fixes their current position as the new 0
  //the movement +=1 increments it to tick the
  //loop to next move function;
}

void loop() {
  int steps = random(0, 500);
  int steps2 = random(0, 500); //generate random step increments for moveLeft/Right
  Serial.print("Current Left Pos:"); //testing
  Serial.println(stepperLeft.currentPosition());
  Serial.print("Current Right Pos:");
  Serial.println(stepperRight.currentPosition());
  Serial.print("Current distance to go:");
  Serial.println(stepperLeft.distanceToGo()); //testing
  if (stepperLeft.distanceToGo() == 0 || stepperRight.distanceToGo() == 0)
  {
    //This as far as I can tell works exactly how I've written it;
    //in that if either stepper has reached the target distance,
    //then it will call reset and stop the motors
    reset();
  }

  if (movement == 0) {
    moveBackward(); //Run them backward first
  }

  if (movement == 1)
  {
    moveForward(); //after first successful run, run them forward
  }

  else {
    moveRight(steps);
    moveLeft(steps2); // make them turn randomly at same time
  }

  stepperRight.run(); //Docs say this will only do stuff when theres a step
  stepperLeft.run(); //to be completed; therefore, as moveTo is absolute
  // and reset sets target at 0, these will only do stuff
  // when theres actual distance to cover (as assigned above)
}

I've put in comments throughout detailing what each step does; the examples and documentation don't conflict this, yet it still doesn't work. Is there something crucial I have missed here? I've been testing all night and tried many approaches but only the approach where run is in the functions works; I feel like I have missed a very subtle point but no amount of searching is revealing what is wrong with this logic?

Alright, correct me if I'm wrong but this program should work:

For some unspecified definition of work, I guess.

The program does something that you have not told us about.

You expect the program to do something that you have not told us about.