Control motor motions order

I’m very new to arduino and have to work on a project for school. We have connected 3 motors (Servo, Stepper (180 degrees) and a DC motor) to an Arduino Uno, Adafruit motorshield and a pcb. With the pcb we were able to record values of the motors individually and use these values to create a playback motion. The code which we got for the playback motion is below (I’ve already filled in my values for the 3 motors).

This is what I want to do with the motors:

When I press a button I want to ‘start’ the cycle of motor motions

We were told not to use a delay and someone suggested to use millis() instead of delay() but since I am a beginner I have no idea where to start. If anything is unclear or if I have not yet explained anything that is important to be able to help me with this, please let me know!

This is the code:

// With this code it is possible to play back recorded motion paths for for motors connected to the Adafruit motorshield

#include <AccelStepper.h>
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include <Servo.h> // this is a library that allows you to drive servo's.

// variables
int modeChoice = 0;

int buttons = A2;  // the three buttons share an analog pin, resitors provide different levels that point to which button is pressed
int slide = A1;    // the slide is read through one analog pin, resitors provide different levels that point to which slide position is current
int rotation = A0; // the pin for the potentiometer

int buttonPressed = 0; // a variable to remember which button is pressed, it is used to control leds and motion memory position management

int buttonValRaw = 0; // a variable to store the raw analog reading of the buttons pin
int slideValRaw = 0;  // a variable to sore the raw analog reading of the slide pin
int valButton = 0;    // a variable to store the interpreted value, the exact pointer to a button
int valSlide = 0;     // a variable to store the interpreted value, the exact pointer to a slide position
int idleState = 0;    // a variable to toggle the servo idle behavior
int resetMotion = 0;  // a variable to return the arrayPosition to 0 if a button is pushed so that the behaviour starts at the beginning
int motionVar1;       // a variable to hold a motion position
int motionVar2;       // another variable to hold a motion position

int mySensorArray[20];  // an array with 20 slots to store raw potentiometer readings (for filtering)
int sensorArrayPos = 0; // the array position of interest.



//////////////////////////////////////

int servoMotionArray_B1[50] = {95, 95, 95, 95, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // paste your 50 values in between the brackets

int DCMotionArray_B1[50] = {-85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85};

//recorded motions for position implementation
int StepperMotionArray_B1[50] = {-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200};

//////////////////////////////////////




int arrayPosition = 0;

unsigned long mainTimer = 0; // a variable to time recording and playback
int timeBetweenSamples = 100; // the time between samples.

unsigned long debounceTimer = 0; // a variable to control a timer interval for debouncing of the buttons
int lastValButton = 0; // a variable to help debouncing of the buttons
int debounceFlag = 0; // another variabel to help debouncing of the buttons



// Setup motors
// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
// Connect a stepper motor with 16 steps per revolution (2048 positions) to motor port #1 (M1 and M2) -- you need to change this if your stepper is on port #2
Adafruit_StepperMotor *myStepper = AFMS.getStepper(16, 1);
// And connect a DC motor to port M3 -- you need to change this if your DC is on a different port
Adafruit_DCMotor *myMotor = AFMS.getMotor(3);

Servo myservo1;   // servo object (employs the servo library that was included above)

// these are dedicated functions to make the steppermotor move
void forwardstep1() {
  myStepper->onestep(FORWARD, SINGLE);
}
void backwardstep1() {
  myStepper->onestep(BACKWARD, SINGLE);
}

//create aStepper object
AccelStepper aStepper(forwardstep1, backwardstep1); // use functions to step


// setup
void setup() {
  Serial.begin(9600); // start serial communication with 9600 baud rate


  AFMS.begin();  // create with the default frequency 1.6KHz
  //AFMS.begin(1000);  // OR with a different frequency, say 1KHz

  myservo1.attach(9); // attach servo object to pin 9

  // turn on motor M3
  myMotor->setSpeed(0);
  myMotor->run(RELEASE);

  // setup the stepper
  aStepper.setSpeed(0); // this is used in the 'speed' implementation

  aStepper.setMaxSpeed(200.0); // these are used in the 'position' implementation
  aStepper.setAcceleration(100.0);
  aStepper.moveTo(0);

  // synchronize the first timer cycle -- because the timer is starting at zero it generates negative values in the first 100 miliseconds after starting up when going through the timer cycle, creating potential erratic bahaviour-- giving it a delay at the end of the setup loop will eradicate this behaviour
  delay(1000);
}

void doButtons() {
  if (buttonPressed != 0) {
    return;
  }
  else {
    buttonValRaw = analogRead(buttons);
    valButton = map (buttonValRaw, 49, 605, 0, 5);
    int localButtonVal = valButton;

    if (valButton > 7) {
      return;
    }

    if (debounceFlag == 0) {
      debounceTimer = millis();
      debounceFlag = 1;
    }

    if (debounceTimer < millis() - 20) {

      buttonValRaw = analogRead(buttons);
      valButton = map (buttonValRaw, 49, 605, 0, 5);

      if (valButton == localButtonVal) {
        debounceFlag = 0;

        switch (valButton) {
          case 1:
            buttonPressed = 1;
            break;
          case 2:
            buttonPressed = 2;
            break;
          case 3:
            buttonPressed = 3;
            break;
          default:
            buttonPressed = 0;
            break;
        }
      }
    }
  }
}

void loop() {
  doButtons(); // this uses the buttons from the PCB, if you use your own implementation you need to reprogram this function

  if (mainTimer < millis() - timeBetweenSamples) { // sampling only once per interval.
    mainTimer = millis(); // time is rewritten to current millis starting a new interval.

    if (buttonPressed == 0) { //'idle play'
      if (idleState == 0) {
        motionVar1 = servoMotionArray_B1[arrayPosition];
      }
      else if (idleState == 1){
        motionVar1 = servoMotionArray_B1[arrayPosition];
      }
      motionVar2 = DCMotionArray_B1[arrayPosition];
      myservo1.write(motionVar1);
      if (motionVar2 < 0) {
        myMotor->run(BACKWARD);
        myMotor->setSpeed(abs(motionVar2));
      }
      else if (motionVar2 >= 0) {
        myMotor->run(FORWARD);
        myMotor->setSpeed(motionVar2);
      }
      arrayPosition++;
      if (arrayPosition > 49) {
        arrayPosition = 0;
        idleState = !idleState;
      }
    }

    if (buttonPressed == 1) { //2nd behaviour
      if (resetMotion == 0) {
        arrayPosition = 0;
        resetMotion = 1;
        myMotor->run(RELEASE); // release the DC motor, otherwise it will continue spinning at the last known RPM
      }
      motionVar1 = servoMotionArray_B3[arrayPosition];
      motionVar2 = StepperMotionArray_B1[arrayPosition];
      myservo1.write(motionVar1);
      aStepper.moveTo(motionVar2);
      arrayPosition++;
      if (arrayPosition > 49) {
        arrayPosition = 0;
        buttonPressed = 0;
        resetMotion = 0;
      }
    }
  }



  // one of these needs to be called to make the stepper motor work, comment out the one you do not need (position or speed)
  //aStepper.run(); //stepper position implementation
  aStepper.runSpeed(); //stepper speed implementation

}

That code looks like it might do much of what you describe. Have you tried to test it? What results do you get?

Steve

I have tested it and it works indeed. The problem at the moment is that the code in void loop() was an example of the motor motions but is not yet adjusted to how I want the motors to behave like I explained in the questions (first the DC motor and stop after 5 seconds, then the servo motor, then the Stepper motor and stop after 5 seconds). I don't know exactly how to adjust the void loop() code to how I want the motors to behave instead of how they currently behave due to the example code.

The main thing which I am trying to figure out now is why my stepper motor doesn't start moving at a speed of -200 (like stated in the values) when I press the button. I have chosen the stepper.runSpeed code at the end of the code and commented out the other one cause that's for position and not what I need, but I don't know what else to do to make it start moving at that speed when I press the button.

Hopefully this is a little bit clear

vkwochi:
We were told not to use a delay and someone suggested to use millis() instead of delay() but since I am a beginner I have no idea where to start.

Have a look at how millis() is used to manage timing without blocking in Several Things at a Time.

And see Using millis() for timing. A beginners guide if you need more explanation.

...R

Thank you for sharing the ‘Several Things at a time’ and Millis explanation! This is what I managed to create so far and it works almost perfectly. There is just one problem left: Right now I said in the code that, whenever you push the button, the stepper motor will always stop working after 20 seconds timed from the very start. This kinda works okay if I press the button at the right time to start the stepper, but it would be better if the stepper always works for 10 seconds after pressing the button, no matter when I press the button.

(long story short: now the stepper motor will not rotate anymore if I wait to press the button after the 20 seconds because of this code:

 if (millis() > timeVar + 20000){    // Make stepper motion stop after 20 seconds
    aStepper.setSpeed(RELEASE);
  }

but I always want to be able to press the button which will cause the stepper to do it’s motion for 10 seconds whenever I want)

Does anyone know how to create this behaviour? this is my code in the void loop() right now:

void loop() {
  doButtons(); // this uses the buttons from the PCB, if you use your own implementation you need to reprogram this function

  if (mainTimer < millis() - timeBetweenSamples) { // sampling only once per interval
    mainTimer = millis();                          // time is rewritten to current millis starting a new interval.

    // Servo Idle states are being used to play multiple behaviours after eachother: create a behaviour of 100 steps total with 2 idle states instead of 50 steps with 1 idle state

    if (buttonPressed == 0) {                            //'idle play' (not having pressed any of the buttons): this is an ongoing cycle of repeated behaviours
      if (idleState == 0) {                              // first idle state behaviour of servo
        motionVar1 = servoMotionArray_B1[arrayPosition]; // let servo stay in starting position
      }
      else if (idleState == 1){                          // second idle state behaviour of servo
        motionVar1 = servoMotionArray_B2[arrayPosition]; // move servo to 'up' position
      }
      else if (idleState == 2){                          // Let servo stay in 'up' position
        motionVar1 = servoMotionArray_B3[arrayPosition];
       
      }
      motionVar2 = DCMotionArray_B1[arrayPosition];      // Let DC do the motion
      myservo1.write(motionVar1);                        // Let Servo do the motion
       
      if (motionVar2 < 0) {
        myMotor->run(BACKWARD);              // negative values make DC turn to the left
        myMotor->setSpeed(abs(motionVar2));
      }
      else if (motionVar2 >= 0) {
        myMotor->run(FORWARD);               // positive values make DC turn to the right
        myMotor->setSpeed(motionVar2);
      }

   //   if (resetMotion == 0) {              // Make Stepper stop moving in 'idle play', otherwise it will continue spinning at the last known speed
   //     aStepper.setSpeed(RELEASE);
   //   } 
      
      arrayPosition++;
      if (arrayPosition > 49) { 
        arrayPosition = 0;
       // idleState = !idleState; (original code)
       idleState = 2; // leave the servo in this position (not going back to other idle states anymore)
      }
    }


    if (millis() > timeVar + 6000){    // Make DC stop after chosen time (6 sec)
    myMotor->run(RELEASE);
    }


    if (buttonPressed == 1) { //2nd behaviour (pressing first button)
      
      if (resetMotion == 0) { // behaviour starts at the beginning
        arrayPosition = 0;
        resetMotion = 1;
        myMotor->run(RELEASE); // release the DC motor, otherwise it will continue spinning at the last known RPM
      }                        // DC stops moving
      
      motionVar2 = StepperMotionArray_B1[arrayPosition];
      aStepper.setSpeed(motionVar2);  // use setSpeed to control speed instead of position for Stepper
      arrayPosition++;
      if (arrayPosition > 49) { // When recorded motions are finished...
        arrayPosition = 0;
        //buttonPressed = 0;  // comment out so it does not go back to servo/DC motions
        resetMotion = 0;
      }
    }
  }



  if (millis() > timeVar + 20000){    // Make stepper motion stop after 20 seconds
    aStepper.setSpeed(RELEASE);
  }



  // one of these needs to be called to make the stepper motor work, comment out the one you do not need (position or speed)
  //aStepper.run(); //stepper position implementation
  aStepper.runSpeed(); //stepper speed implementation

}

This NOT the way to use millis()

  if (millis() > timeVar + 20000){    // Make stepper motion stop after 20 seconds

  }

You should ALWAYS use subtraction - like this as it avoids a problem when millis() rolls over

  if (millis() - timeVar > 20000){    // Make stepper motion stop after 20 seconds
    
  }

, but it would be better if the stepper always works for 10 seconds after pressing the button, no matter when I press the button.

As you have not posted the complete program I can't help with that. The piece you have posted does not show where the variable timeVar is updated.

In general if you want something to be timed from when a button is pressed you would record the time when the button is pressed - like this pseudo code

if (buttonPressed == true) {
   timeButtonPressed = millis();
   // other code
}
if (millis() - timeButtonPressed >= interval) {
  // time is up
  // do something
}

...R

Robin2 thanks for the reply again! I just changed my code to use the millis properly and this is what I have, there is still a problem at the end when the button is pressed that the stepper motor does not stop after the ‘period’ so there is something wrong there which I don’t understand:

#include <AccelStepper.h>
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include <Servo.h> // this is a library that allows you to drive servo's.

// variables
int modeChoice = 0;

int buttons = A2;  // the three buttons share an analog pin, resitors provide different levels that point to which button is pressed
int slide = A1;    // the slide is read through one analog pin, resitors provide different levels that point to which slide position is current
int rotation = A0; // the pin for the potentiometer

int buttonPressed = 0; // a variable to remember which button is pressed, it is used to control leds and motion memory position management

int buttonValRaw = 0; // a variable to store the raw analog reading of the buttons pin
int slideValRaw = 0;  // a variable to sore the raw analog reading of the slide pin
int valButton = 0;    // a variable to store the interpreted value, the exact pointer to a button
int valSlide = 0;     // a variable to store the interpreted value, the exact pointer to a slide position
int idleState = 0;    // a variable to toggle the servo idle behavior
int resetMotion = 0;  // a variable to return the arrayPosition to 0 if a button is pushed so that the behaviour starts at the beginning
int motionVar1;       // a variable to hold a motion position
int motionVar2;       // another variable to hold a motion position

int mySensorArray[20];  // an array with 20 slots to store raw potentiometer readings (for filtering)
int sensorArrayPos = 0; // the array position of interest.

const unsigned long period = 10000; 
unsigned long timeButtonPressed; 
unsigned long currentMillis;
unsigned long startTime;  

int servoMotionArray_B1[50] = {95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95};
int servoMotionArray_B2[50] = {95, 95, 95, 95, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // paste your 50 values in between the brackets
int servoMotionArray_B3[50] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

int DCMotionArray_B1[50] = {-85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85};

//recorded motions for position implementation
int StepperMotionArray_B1[50] = {-200, -200, -200, -200, -200, -200, -200, -200, -200 ,-200, -200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200,-200};

int arrayPosition = 0;

unsigned long mainTimer = 0; // a variable to time recording and playback
int timeBetweenSamples = 100; // the time between samples.

unsigned long debounceTimer = 0; // a variable to control a timer interval for debouncing of the buttons
int lastValButton = 0; // a variable to help debouncing of the buttons
int debounceFlag = 0; // another variabel to help debouncing of the buttons



// Setup motors
// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
// Connect a stepper motor with 16 steps per revolution (2048 positions) to motor port #1 (M1 and M2) -- you need to change this if your stepper is on port #2
Adafruit_StepperMotor *myStepper = AFMS.getStepper(16, 1);
// And connect a DC motor to port M3 -- you need to change this if your DC is on a different port
Adafruit_DCMotor *myMotor = AFMS.getMotor(3);

Servo myservo1;   // servo object (employs the servo library that was included above)

// these are dedicated functions to make the steppermotor move
void forwardstep1() {
  myStepper->onestep(FORWARD, SINGLE);
}
void backwardstep1() {
  myStepper->onestep(BACKWARD, SINGLE);
}

//create aStepper object
AccelStepper aStepper(forwardstep1, backwardstep1); // use functions to step


// setup
void setup() {
  Serial.begin(9600); // start serial communication with 9600 baud rate


  AFMS.begin();  // create with the default frequency 1.6KHz
  //AFMS.begin(1000);  // OR with a different frequency, say 1KHz

  myservo1.attach(9); // attach servo object to pin 9

  // turn on motor M3
  myMotor->setSpeed(0);
  myMotor->run(RELEASE);

  // setup the stepper
  aStepper.setSpeed(0); // this is used in the 'speed' implementation 

  aStepper.setMaxSpeed(200.0); // these are used in the 'position' implementation 
  aStepper.setAcceleration(100.0);
  aStepper.moveTo(0);

  // synchronize the first timer cycle -- because the timer is starting at zero it generates negative values in the first 100 miliseconds after starting up when going through the timer cycle, creating potential erratic bahaviour-- giving it a delay at the end of the setup loop will eradicate this behaviour
  delay(1000);

  startTime = millis(); // initial start time
}

void doButtons() {
  if (buttonPressed != 0) {
    return;
  }
  else {
    buttonValRaw = analogRead(buttons);
    valButton = map (buttonValRaw, 49, 605, 0, 5);
    int localButtonVal = valButton;

    if (valButton > 7) {
      return;
    }

    if (debounceFlag == 0) {
      debounceTimer = millis();
      debounceFlag = 1;
    }

    if (debounceTimer < millis() - 20) {

      buttonValRaw = analogRead(buttons);
      valButton = map (buttonValRaw, 49, 605, 0, 5);

      if (valButton == localButtonVal) {
        debounceFlag = 0;

        switch (valButton) {
          case 1:
            buttonPressed = 1;
            break;
          case 2:
            buttonPressed = 2;
            break;
          case 3:
            buttonPressed = 3;
            break;
          default:
            buttonPressed = 0;
            break;
        }
      }
    }
  }
}

void loop() {
  doButtons(); // this uses the buttons from the PCB, if you use your own implementation you need to reprogram this function

  if (mainTimer < millis() - timeBetweenSamples) { // sampling only once per interval
    mainTimer = millis();                          // time is rewritten to current millis starting a new interval

    if (buttonPressed == 0) {                            //'idle play' 
      if (idleState == 0) {                              
        motionVar1 = servoMotionArray_B1[arrayPosition]; 
      }
      else if (idleState == 1){                         
        motionVar1 = servoMotionArray_B2[arrayPosition]; 
      }
      else if (idleState == 2){                         
        motionVar1 = servoMotionArray_B3[arrayPosition];
       
      }
      motionVar2 = DCMotionArray_B1[arrayPosition];      
      myservo1.write(motionVar1);                        
       
      if (motionVar2 < 0) {
        myMotor->run(BACKWARD);              // negative values make DC turn to the left
        myMotor->setSpeed(abs(motionVar2));
      }
      else if (motionVar2 >= 0) {
        myMotor->run(FORWARD);               // positive values make DC turn to the right
        myMotor->setSpeed(motionVar2);
      }
      arrayPosition++;
      if (arrayPosition > 49) { 
        arrayPosition = 0;
       idleState = 2; 
      }
    }
    currentMillis = millis();        // after period had passed DC turns off
    if (currentMillis - startTime >= period){
      myMotor->run(RELEASE);
    }


    if (buttonPressed == 1) { //2nd behaviour (pressing first button)

      timeButtonPressed = millis();
      
      if (resetMotion == 0) {
        arrayPosition = 0;
        resetMotion = 1;
        myMotor->run(RELEASE); // release the DC motor, otherwise it will continue spinning at the last known RPM
      }                        
      
      motionVar2 = StepperMotionArray_B1[arrayPosition];
      aStepper.setSpeed(motionVar2);  
      arrayPosition++;
      if (arrayPosition > 49) { 
        arrayPosition = 0;
        resetMotion = 0;
      }
    }
     if (millis() - timeButtonPressed >= period) {        // if button is pressed the stepper moves for period time
       aStepper.setSpeed(RELEASE);
  }
}

  aStepper.runSpeed(); //stepper speed implementation
}

What’s the purpose of this line which seems to envelope most of the code?

  if (mainTimer < millis() - timeBetweenSamples) {

As well as the fact that millis() is not being used properly I strongly suspect either {A} this code in unnecessary or {B} it should only apply to a small section of the code.

I am not familar with how Adafruit controls stepper motors so I don’t know if this line is correct or what RELEASE actually does.

aStepper.setSpeed(RELEASE);

One would not normally “release” a stepper motor in case it misses a step.

…R

To be completely honest, I have no idea what 'mainTimer' exactly does, since this is from the original code which we received to work with. I've tried to leave it out but that causes problems.

RELEASE causes the motors to stop. (I've also tried setSpeed(0) for example, but this didn't make the motor stop)

vkwochi:
To be completely honest, I have no idea what 'mainTimer' exactly does, since this is from the original code which we received to work with. I've tried to leave it out but that causes problems.

I doubt if you will solve this problem until you figure out what mainTimer does.

As you have not told us what problems are caused I cannot offer suggestions.

...R