Simultaneous Nema 17 Stepper Motors with MITAppInventor

Hey guys, I'm having a problem with my switch cases, whereby certain functions within the switch case take upwards of 2 seconds to react/execute.

For example, in case 1 and case 2, DETECTED == HIGH is when there is no object infront of the laser receiver, and DETECTED == LOW is when there is an object infront of the laser receiver. This will then trigger another function. Unfortunately, it takes upwards of 2 seconds for DETECTED == HIGH to change to LOW when I block the laser module, whilst either case 1 or case 2 are triggered (via serial monitor).

Currently my serial monitor is set to "No Line Ending".

My question is, is there a way to loop through these switch cases faster?

#include <AccelStepper.h>

const int stepsPerRevolution = 200;

AccelStepper ballHopperStepper = AccelStepper (2, 3, 4);
AccelStepper oscillationStepper = AccelStepper (5, 6, 7);
AccelStepper angleStepper = AccelStepper (8, 9, 10);

#define DETECT 28 // pin 28 for  sensor
#define pwmPin1 12
#define pwmPin2 13
#define controlPin1 A0
#define controlPin2 A1
#define stepsPerRevolution 200
int val = 0;
int state = 0;
int start = 0;
int stop1 = 49;
int count = 0;
unsigned long previousMillis = 0;
const long interval = 1;


void setup() {
  pinMode(DETECT, INPUT);
  ballHopperStepper.setMaxSpeed(2000);
  ballHopperStepper.setAcceleration(2000);
  oscillationStepper.setMaxSpeed(1000);
  oscillationStepper.setAcceleration(500);
  angleStepper.setMaxSpeed(1000);
  angleStepper.setAcceleration(500);
  Serial.begin(9600);
}

bool myFlag = false;

void loop() {
  unsigned long currentMillis = millis();


  if (Serial.available() > 0) {
    state = Serial.read() - 48;
    if (state == 3) myFlag = true;
  }
  int detected = digitalRead(DETECT);

  //Serial.println(ballHopperStepper.currentPosition());

  if ((state < 0) || (state > 9)) {
    state = 0; //return to idle
  } else {

    switch (state) {  // Beginner Start Button Pressed

      case 0:
        Serial.println("Waiting for user to select mode");
        break;

      case 1:
        spinMainMotors();
        rotate25Beginner();
        if (currentMillis - previousMillis >= interval && detected == LOW) {
          previousMillis = currentMillis;
          rotate90Beginner();
        } else {
          Serial.println("Waiting for ball in play1");
        }

        break;


      case 2:  // Intermediate Start Button Pressed
        spinMainMotors();
        rotate25Advanced();
        if (detected == LOW) {
          rotate90Advanced();
        } else {
          Serial.println("Waiting for ball in play2");
        }


      case 3: // Shoot One Ball
        if (myFlag == true) {
          spinMainMotors();
          rotate90Advanced();
          myFlag = false;
        }
        break;


      case 4:
        Serial.println("Ball detected!");
        ballHopperStepper.moveTo(ballHopperStepper.currentPosition() + 400);
        ballHopperStepper.runToPosition();
        myFlag = false;
        break;
    }
  }
}

void rotate90Beginner() {
  Serial.println("Ball detected!");
  //delay(1000);
  ballHopperStepper.moveTo(ballHopperStepper.currentPosition() + 400);
  ballHopperStepper.run();
  //delay(1000);
}

void rotate90Advanced() {
  Serial.println("Ball detected!");
  ballHopperStepper.moveTo(ballHopperStepper.currentPosition() + 400);
  ballHopperStepper.runToPosition();
}

void rotate25Beginner() {
  Serial.println("Rotating");
  oscillationStepper.moveTo(ballHopperStepper.currentPosition() - 111);
  oscillationStepper.run();
  oscillationStepper.moveTo(ballHopperStepper.currentPosition() + 111);
  oscillationStepper.run();
}

void rotate25Advanced() {
  Serial.println("Rotating");
  oscillationStepper.moveTo(ballHopperStepper.currentPosition() - 111);
  oscillationStepper.runToPosition();
  oscillationStepper.moveTo(ballHopperStepper.currentPosition() + 111);
  oscillationStepper.runToPosition();
}


void spinMainMotors() {
  i

My question is, is there a way to loop through these switch cases faster?

That sounds like the wrong question. Should you not be asking if there is a faster way to read the laser ?

Why do you have 2 seconds of delay() in rotate90Beginner() ?

If you are in state 1 and detect is LOW then you will have 2 seconds of delay because of the delays in rotate90Beginner function.

UKHeliBob:
That sounds like the wrong question. Should you not be asking if there is a faster way to read the laser ?

Why do you have 2 seconds of delay() in rotate90Beginner() ?

Ah yes...because Rotate90Beginner() is supposed to be "slower" than the advanced mode.

ToddL1962:
If you are in state 1 and detect is LOW then you will have 2 seconds of delay because of the delays in rotate90Beginner function.

but it should still print to the serial monitor "ball detected" without delay. This is not the case, as it still takes upwards of 2 seconds in Case 2 as well.

all your runToPosition() calls also block until completed. How long do they take to execute? While you are moving your steppers, you aren't checking your sensor.

blh64:
all your runToPosition() calls also block until completed. How long do they take to execute? While you are moving your steppers, you aren't checking your sensor.

ah interesting..that makes sense. Ideally, all functions in each case need to run simultaneously (or within a couple milliseconds of each other). I'm not sure how to achieve this. If anyone could point me in the right direction, I'd truly appreciate it :slight_smile:

You've chosen to use blocking code techniques. For example, from AccelStepper.h:

   /// Moves the motor (with acceleration/deceleration) 
    /// to the target position and blocks until it is at
    /// position. Dont use this in event loops, since it blocks.
    void    runToPosition();

Your code will need to be rewritten to use non-blocking techniques, such as:

   /// Poll the motor and step it if a step is due, implementing
    /// accelerations and decelerations to acheive the target position. You must call this as
    /// frequently as possible, but at least once per minimum step time interval,
    /// preferably in your main loop. Note that each call to run() will make at most one step, and then only when a step is due,
    /// based on the current speed and the time since the last step.
    /// \return true if the motor is still running to the target position.
    boolean run();

gfvalvo:
You've chosen to use blocking code techniques. For example, from AccelStepper.h:

   /// Moves the motor (with acceleration/deceleration) 

/// to the target position and blocks until it is at
    /// position. Dont use this in event loops, since it blocks.
    void    runToPosition();




Your code will need to be rewritten to use non-blocking techniques, such as:


/// Poll the motor and step it if a step is due, implementing
    /// accelerations and decelerations to acheive the target position. You must call this as
    /// frequently as possible, but at least once per minimum step time interval,
    /// preferably in your main loop. Note that each call to run() will make at most one step, and then only when a step is due,
    /// based on the current speed and the time since the last step.
    /// \return true if the motor is still running to the target position.
    boolean run();




ah many thanks! I have tried using run() instead of runToPosition(), and it appears to be looping through faster, however, it is still upwards of 2 seconds to change DETECTED from HIGH to LOW when blocking the laser receiver. I need it to change in milliseconds.

Of course. You need to restructure the whole code as non-blocking.

AccelStepper is designed to be used non-blocking, just never use the blocking calls.

Place run() in loop(), use moveTo(), move(), stop(), distanceToGo(), currentPosition(), targetPosition()

Your code needs to be one or more state-machines, checking for transitions between states every time
through loop(), but never waiting or delaying. Then the call to run() happens very frequently and the
library does it job behind the scenes.

MarkT:
AccelStepper is designed to be used non-blocking, just never use the blocking calls.

Place run() in loop(), use moveTo(), move(), stop(), distanceToGo(), currentPosition(), targetPosition()

Your code needs to be one or more state-machines, checking for transitions between states every time
through loop(), but never waiting or delaying. Then the call to run() happens very frequently and the
library does it job behind the scenes.

ohhhh ok ok I see. I've changed my runToPosition() in anything corresponding to case 1, and everything is responding so much faster! Thank you so much. I noticed one interesting thing though...if I use run() in my rotate90Beginner() function, the stepper motor (nema 17 with TB6600) does not turn, however, when I change that back to runToPosition(), it works nicely.

EDIT: Okay, so i've just set up my second stepper motor (oscillationStepper) with another new TB6600, but only ballHopperStepper is working as normal; oscillationStepper is moving very very slowly. Just confirmed that the hardware is fine...swapped over everything.

Another thing I noticed is that when I comment out either rotate90Beginner() or rotate25Beginner() and run the code, ballHopperStepper stepper motor takes over the function for either one. I'm still seeing the oscillationStepper stepper motor moving slowly as well. WTF? lol

#include <AccelStepper.h>

const int stepsPerRevolution = 200;

AccelStepper ballHopperStepper = AccelStepper (2, 3, 4);
AccelStepper oscillationStepper = AccelStepper (5, 6, 7);
AccelStepper angleStepper = AccelStepper (8, 9, 10);

#define DETECT 28 // pin 28 for  sensor
#define pwmPin1 12
#define pwmPin2 13
#define controlPin1 A0
#define controlPin2 A1
#define stepsPerRevolution 200
int val = 0;
int state = 0;
int start = 0;
int stop1 = 49;
int count = 0;
unsigned long previousMillis = 0;
const long interval = 1;


void setup() {
  pinMode(DETECT, INPUT);
  ballHopperStepper.setMaxSpeed(2000);
  ballHopperStepper.setAcceleration(2000);
  oscillationStepper.setMaxSpeed(1000);
  oscillationStepper.setAcceleration(500);
  angleStepper.setMaxSpeed(1000);
  angleStepper.setAcceleration(500);
  Serial.begin(9600);
}

bool myFlag = false;

void loop() {
  unsigned long currentMillis = millis();


  if (Serial.available() > 0) {
    state = Serial.read() - 48;
    if (state == 3) myFlag = true;
  }
  int detected = digitalRead(DETECT);

  //Serial.println(ballHopperStepper.currentPosition());

  if ((state < 0) || (state > 9)) {
    state = 0; //return to idle
  } else {

    switch (state) {  // Beginner Start Button Pressed

      case 0:
        Serial.println("Waiting for user to select mode");
        break;

      case 1:
        spinMainMotors();
        rotate25Beginner();
        if (currentMillis - previousMillis >= interval && detected == LOW) {
          previousMillis = currentMillis;
          rotate90Beginner();
        } else {
          Serial.println("Waiting for ball in play1");
        }

        break;


      case 2:  // Intermediate Start Button Pressed
        spinMainMotors();
        rotate25Advanced();
        if (detected == LOW) {
          rotate90Advanced();
        } else {
          Serial.println("Waiting for ball in play2");
        }


      case 3: // Shoot One Ball
        if (myFlag == true) {
          spinMainMotors();
          rotate90Advanced();
          myFlag = false;
        }
        break;


      case 4:
        Serial.println("Ball detected!");
        ballHopperStepper.moveTo(ballHopperStepper.currentPosition() + 400);
        ballHopperStepper.runToPosition();
        myFlag = false;
        break;
    }
  }
}

void rotate90Beginner() {
  Serial.println("Ball detected!");
  //delay(1000);
  ballHopperStepper.moveTo(ballHopperStepper.currentPosition() + 400);
  ballHopperStepper.run();
  //delay(1000);
}

void rotate90Advanced() {
  Serial.println("Ball detected!");
  ballHopperStepper.moveTo(ballHopperStepper.currentPosition() + 400);
  ballHopperStepper.runToPosition();
}

void rotate25Beginner() {
  Serial.println("Rotating");
  oscillationStepper.moveTo(ballHopperStepper.currentPosition() - 111);
  oscillationStepper.run();
  oscillationStepper.moveTo(ballHopperStepper.currentPosition() + 111);
  oscillationStepper.run();
}

void rotate25Advanced() {
  Serial.println("Rotating");
  oscillationStepper.moveTo(ballHopperStepper.currentPosition() - 111);
  oscillationStepper.runToPosition();
  oscillationStepper.moveTo(ballHopperStepper.currentPosition() + 111);
  oscillationStepper.runToPosition();
}


void spinMainMotors() {
  int potValue1 = analogRead(controlPin1);
  int potValue2 = analogRead(controlPin2);

  int pwm1 = map(potValue1, 0, 1023, 0, 255);
  int pwm2 = map(potValue2, 0, 1023, 0, 255);
  pwm1 = toPWM(80);
  analogWrite(pwmPin1, pwm1);
  Serial.print("PWM1: ");
  Serial.print(pwm1);
  pwm2 = toPWM(80);
  analogWrite(pwmPin2, pwm2);
  Serial.print("PWM2: ");
  Serial.print(pwm2);
}

int toPWM(int v) {
  return map(v, 0, 100, 0, 255);
}

Another thing I noticed is that when I comment out either rotate90Beginner() or rotate25Beginner() and run the code, ballHopperStepper stepper motor takes over the function for either one. I'm still seeing the oscillationStepper stepper motor moving slowly as well. WTF? lol

Hey guys, when I comment out either rotate90Beginner() or rotate25Beginner() and run the code (run case 1 via serial monitor), oscillationStepper stepper motor takes over the function for either function (using Arduino Mega). The ballHopperStepper stepper motor moving slowly simultaneously. What's supposed to happen is oscillationStepper stepper motor is supposed to run rotate25Beginner() function, whilst ballHopperStepper stepper motor is supposed to run rotate90Beginner() function simultaneously.

When I try changing runToPosition() to run(), motor does not move at all.

It's like if the ball hopper stepper motor has a virus, and is confused.

WTF? lol

#include <AccelStepper.h>

const int stepsPerRevolution = 200;

AccelStepper oscillationStepper = AccelStepper (2, 3, 4);
AccelStepper ballHopperStepper = AccelStepper (8, 9, 10);
AccelStepper angleStepper = AccelStepper (5, 6, 7);

#define DETECT 28 // pin 28 for  sensor
#define pwmPin1 12
#define pwmPin2 13
#define controlPin1 A0
#define controlPin2 A1
#define stepsPerRevolution 200
int val = 0;
int state = 0;
int start = 0;
int stop1 = 49;
int count = 0;
unsigned long previousMillis = 0;
const long interval = 1;


void setup() {
  pinMode(DETECT, INPUT);
  ballHopperStepper.setMaxSpeed(2000);
  ballHopperStepper.setAcceleration(2000);
  oscillationStepper.setMaxSpeed(2000);
  oscillationStepper.setAcceleration(2000);
  angleStepper.setMaxSpeed(2000);
  angleStepper.setAcceleration(2000);
  Serial.begin(9600);
}

bool myFlag = false;

void loop() {
  unsigned long currentMillis = millis();


  if (Serial.available() > 0) {
    state = Serial.read() - 48;
    if (state == 3) myFlag = true;
  }
  int detected = digitalRead(DETECT);

  //Serial.println(ballHopperStepper.currentPosition());

  if ((state < 0) || (state > 9)) {
    state = 0; //return to idle
  } else {

    switch (state) {

      case 0:
        Serial.println("Waiting for user to select mode");
        break;

      case 1:  // Beginner Start Button Pressed

        if (currentMillis - previousMillis >= interval && detected == LOW) {
          previousMillis = currentMillis;
          //spinMainMotors();
          rotate90Beginner();
          //rotate25Beginner();
        } else {
          Serial.println("Waiting for ball in play1");
        }

        break;


      case 2:  // Intermediate Start Button Pressed
        spinMainMotors();
        rotate25Advanced();
        if (detected == LOW) {
          rotate90Advanced();
        } else {
          Serial.println("Waiting for ball in play2");
        }


      case 3: // Shoot One Ball
        if (myFlag == true) {
          spinMainMotors();
          rotate25Beginner();
          //rotate90Advanced();
          myFlag = false;
        }
        break;


      case 4:
        Serial.println("Ball detected!");
        ballHopperStepper.moveTo(ballHopperStepper.currentPosition() + 400);
        ballHopperStepper.runToPosition();
        myFlag = false;
        break;
    }
  }
}

void rotate90Beginner() {
  Serial.println("Ball detected!");
  delay(1000);
  ballHopperStepper.moveTo(ballHopperStepper.currentPosition() + 400);
  ballHopperStepper.runToPosition();
  delay(1000);
}

void rotate90Advanced() {
  Serial.println("Ball detected!");
  ballHopperStepper.moveTo(ballHopperStepper.currentPosition() + 400);
  ballHopperStepper.runToPosition();
}

void rotate25Beginner() {
  Serial.println("Rotating Beginner");
  oscillationStepper.moveTo(oscillationStepper.currentPosition() - 200);
  oscillationStepper.runToPosition();
  delay(2000);
  oscillationStepper.moveTo(oscillationStepper.currentPosition() + 200);
  oscillationStepper.runToPosition();
  delay(2000);
}

void rotate25Advanced() {
  Serial.println("Rotating Advanced");
  oscillationStepper.moveTo(oscillationStepper.currentPosition() - 400);
  oscillationStepper.runToPosition();
  oscillationStepper.moveTo(oscillationStepper.currentPosition() + 400);
  oscillationStepper.runToPosition();
}


void spinMainMotors() {
  int potValue1 = analogRead(controlPin1);
  int potValue2 = analogRead(controlPin2);

  int pwm1 = map(potValue1, 0, 1023, 0, 255);
  int pwm2 = map(potValue2, 0, 1023, 0, 255);
  pwm1 = toPWM(80);
  analogWrite(pwmPin1, pwm1);
  Serial.print("PWM1: ");
  Serial.print(pwm1);
  pwm2 = toPWM(80);
  analogWrite(pwmPin2, pwm2);
  Serial.print("PWM2: ");
  Serial.print(pwm2);
}

int toPWM(int v) {
  return map(v, 0, 100, 0, 255);
}

You will need to completely refactor your code. The stepper.run() function needs to be called repeatedly in loop(), not one time inside your other functions. This also means that you should look at the example StateChangeDetection (File->Examples->02.Digital->StateChangeDetection) to learn how to detect when the button got pressed, not if it is pressed.

You should also look up state machines since you will want to do some things when you begin a new state (like a button being pressed) vs. being in a state and checking to see if you are done.

As an example, when state==1, you are starting your movement so you would use the moveTo() function and then transition to another state, in that next state, you don't re-set the position, you just call the run() command and see if the distanceToGo() is zero. If it is, you are done.

AccelStepper::AccelStepper(uint8_t interface = AccelStepper::FULL4WIRE, uint8_t pin1 = 2, ... )
AccelStepper oscillationStepper = AccelStepper (2, 3, 4);
AccelStepper ballHopperStepper = AccelStepper (8, 9, 10);
AccelStepper angleStepper = AccelStepper (5, 6, 7);
  FUNCTION  = 0, ///< Use the functional interface, implementing your own driver functions (internal use only)
 DRIVER    = 1, ///< Stepper Driver, 2 driver pins required
 FULL2WIRE = 2, ///< 2 wire stepper, 2 motor pins required
 FULL3WIRE = 3, ///< 3 wire stepper, such as HDD spindle, 3 motor pins required
 FULL4WIRE = 4, ///< 4 wire full stepper, 4 motor pins required
 HALF3WIRE = 6, ///< 3 wire half stepper, such as HDD spindle, 3 motor pins required
 HALF4WIRE = 8  ///< 4 wire half stepper, 4 motor pins required

What stepper drivers (interface) are you using? Are there really 3 different drivers (there is not driver 5)?

paulsamaroo:
It's like if the ball hopper stepper motor has a virus, and is confused.

What stepper motor drivers are you using?

If they all take step and direction signals then the first parameter in these calls should be 1

AccelStepper oscillationStepper = AccelStepper (2, 3, 4);

...R

I'm using bipolar Nema 17 stepper motors (4 wires) and TB6600 drivers

How do you have them wired up? You should only have 2 pins for each motor (direction and step) and then use interface driver 1 from the AccelStepper library

#define dirPin 2
#define stepPin 3
#define motorInterfaceType 1
AccelStepper stepper = AccelStepper(motorInterfaceType, stepPin, dirPin);

Repeat for each of your motors

blh64:
How do you have them wired up? You should only have 2 pins for each motor (direction and step) and then use interface driver 1 from the AccelStepper library

#define dirPin 2

#define stepPin 3
#define motorInterfaceType 1
AccelStepper stepper = AccelStepper(motorInterfaceType, stepPin, dirPin);




Repeat for each of your motors

Okay so I've changed it to reflect this, however, oscillationStepper is trying to turn when it has to turn, but in very small increments each time and only one direction. Nothing at all from ballHopperStepper.

#include <AccelStepper.h>

const int stepsPerRevolution = 200;


#define dirPin 2
#define stepPin 3
#define motorInterfaceType 1
#define DETECT 28 // pin 28 for  sensor
#define pwmPin1 12
#define pwmPin2 13
#define controlPin1 A0
#define controlPin2 A1
#define stepsPerRevolution 200


AccelStepper oscillationStepper = AccelStepper (1, 3, 4);
AccelStepper ballHopperStepper = AccelStepper (1, 6, 7);
AccelStepper angleStepper = AccelStepper (1, 8, 9);


int val = 0;
int state = 0;
int start = 0;
int stop1 = 49;
int count = 0;
unsigned long previousMillis = 0;
const long interval = 1;


void setup() {
  pinMode(DETECT, INPUT);
  ballHopperStepper.setMaxSpeed(2000);
  ballHopperStepper.setAcceleration(2000);
  oscillationStepper.setMaxSpeed(2000);
  oscillationStepper.setAcceleration(2000);
  angleStepper.setMaxSpeed(2000);
  angleStepper.setAcceleration(2000);
  Serial.begin(9600);
}

bool myFlag = false;

void loop() {
  unsigned long currentMillis = millis();


  if (Serial.available() > 0) {
    state = Serial.read() - 48;
    if (state == 3) myFlag = true;
  }
  int detected = digitalRead(DETECT);

  //Serial.println(ballHopperStepper.currentPosition());

  if ((state < 0) || (state > 9)) {
    state = 0; //return to idle
  } else {

    switch (state) {

      case 0:
        Serial.println("Waiting for user to select mode");
        break;

      case 1:  // Beginner Start Button Pressed

        if (currentMillis - previousMillis >= interval && detected == LOW) {
          previousMillis = currentMillis;
          spinMainMotors();
          rotate90Beginner();
          rotate25Beginner();
        } else {
          Serial.println("Waiting for ball in play1");
        }

        break;


      case 2:  // Intermediate Start Button Pressed
        spinMainMotors();
        rotate25Advanced();
        if (detected == LOW) {
          rotate90Advanced();
        } else {
          Serial.println("Waiting for ball in play2");
        }


      case 3: // Shoot One Ball
        if (myFlag == true) {
          spinMainMotors();
          rotate25Beginner();
          //rotate90Advanced();
          myFlag = false;
        }
        break;


      case 4:
        Serial.println("Ball detected!");
        ballHopperStepper.moveTo(ballHopperStepper.currentPosition() + 400);
        ballHopperStepper.runToPosition();
        myFlag = false;
        break;
    }
  }
}

void rotate90Beginner() {
  Serial.println("Ball detected!");
  delay(1000);
  ballHopperStepper.moveTo(ballHopperStepper.currentPosition() + 400);
  ballHopperStepper.runToPosition();
  delay(1000);
}

void rotate90Advanced() {
  Serial.println("Ball detected!");
  ballHopperStepper.moveTo(ballHopperStepper.currentPosition() + 400);
  ballHopperStepper.runToPosition();
}

void rotate25Beginner() {
  Serial.println("Rotating Beginner");
  oscillationStepper.moveTo(oscillationStepper.currentPosition() - 200);
  oscillationStepper.runToPosition();
  delay(2000);
  oscillationStepper.moveTo(oscillationStepper.currentPosition() + 200);
  oscillationStepper.runToPosition();
  delay(2000);
}

void rotate25Advanced() {
  Serial.println("Rotating Advanced");
  oscillationStepper.moveTo(oscillationStepper.currentPosition() - 400);
  oscillationStepper.runToPosition();
  oscillationStepper.moveTo(oscillationStepper.currentPosition() + 400);
  oscillationStepper.runToPosition();
}


void spinMainMotors() {
  int potValue1 = analogRead(controlPin1);
  int potValue2 = analogRead(controlPin2);

  int pwm1 = map(potValue1, 0, 1023, 0, 255);
  int pwm2 = map(potValue2, 0, 1023, 0, 255);
  pwm1 = toPWM(80);
  analogWrite(pwmPin1, pwm1);
  Serial.print("PWM1: ");
  Serial.print(pwm1);
  pwm2 = toPWM(80);
  analogWrite(pwmPin2, pwm2);
  Serial.print("PWM2: ");
  Serial.print(pwm2);
}

int toPWM(int v) {
  return map(v, 0, 100, 0, 255);
}

Post a complete wiring diagram.