How to make a robot do a predetermined set of actions once after a button push

I am designing a robot car (controlled with an adafruit motor shield) for a science competition and I want it to follow a pre-programed path after pushing a button and then simply stop after it is done, but void loop is tripping me up.

How do I stop It from simply repeating the actions it just did?

here is the code I have so far:

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_MS_PWMServoDriver.h"

unsigned long interval[] = {200,400,600,1000,1200,1400,1600,6200};     // the time we need to wait

// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 

// Connect a stepper motor with 200 steps per revolution (1.8 degree)
// to motor port #2 (M3 and M4)
Adafruit_StepperMotor *myStepper = AFMS.getStepper(200, 1);

Adafruit_DCMotor *myMotor = AFMS.getMotor(3);            // And connect a DC motor to port M1

int switchPin = 3;                                       // switch is connected to pin 2
int val;                                                 // variable for reading the pin status

void setup() {
  // put your setup code here, to run once:
  AFMS.begin();                                          // create with the default frequency 1.6KHz
  //AFMS.begin(1000);                                    // OR with a different frequency, say 1KHz
  myStepper->setSpeed(20);                              // set default stepper motor speed to 100 rpm  
}

void loop() {
  // put your main code here, to run repeatedly:
  unsigned long currentMillis = millis(); // grab current time
  val=digitalRead(switchPin);                            // read input value and store it in val
  if (val == LOW) {                                      // check if the button is pressed
    delay(500);                                          // Wait .5 seconds
    myMotor->run(FORWARD);                               // Turn dc motor on
    myMotor->setSpeed(100);                              // set dc motor speed to moderate pace
    delay(200);                                          // Wait .2 seconds
    myMotor->setSpeed(200);                              // set dc motor speed to full speed
    if ((unsigned long)(currentMillis) >= interval[1]) { // check if "interval" time has passed (200 milliseconds)
      myStepper->step(20, FORWARD, SINGLE);              // runs stepper motor 10 degrees forward
    }   
    if ((unsigned long)(currentMillis) >= interval[2]) { // check if "interval" time has passed (400 milliseconds)
      myStepper->step(40, BACKWARD, SINGLE);             // runs stepper motor 10 degrees backward
    }       
    if ((unsigned long)(currentMillis) >= interval[3]) { // check if "interval" time has passed (600 milliseconds)
      myStepper->step(20, FORWARD, SINGLE);              // runs stepper motor 10 degrees FORWARD, returning to zero
    }          
    if ((unsigned long)(currentMillis) >= interval[4]) { // check if "interval" time has passed (1000 milliseconds)
      myStepper->step(20, BACKWARD, SINGLE);             // runs stepper motor 10 degrees BACKWARD
    }              
    if ((unsigned long)(currentMillis) >= interval[5]) { // check if "interval" time has passed (1200 milliseconds)
      myStepper->step(40, FORWARD, SINGLE);              // runs stepper motor 10 degrees forward
    }
    if ((unsigned long)(currentMillis) >= interval[6]) { // check if "interval" time has passed (1400 milliseconds)
      myStepper->step(20, BACKWARD, SINGLE);             // runs stepper motor 10 degrees backward, returning to zero
    }                      
    if ((unsigned long)(currentMillis) >= interval[7]) { // check if "interval" time has passed (2000 milliseconds)
      myMotor->setSpeed(85);                             // set dc motor speed to at a much slower rate
    }
    if ((unsigned long)(currentMillis) >= interval[8]) { // check if "interval" time has passed (62000 milliseconds)     
      myMotor->run(RELEASE);                             // Turn dc motor off

How do I stop It from simply repeating the actions it just did?

if you only want it to run once, put all the code in setup, or put an infinite loop after the code, or use a simple flag to say you're done.

Hmm, putting it in the setup might work Can you put functions that involve waiting for input in the setup?.

Also, what exactly is a "simple Flag"?

Lastly, say I wanted the car to carry through with its pre-programed route, and then continue straight until it hit a limit switch, how might I be able to achieve that? the car has a screw axle with a nut that advances as it moves, eventually hitting a limit switch (Render Bellow)

|500x337

OK, looking again at your code, if you put the code in loop into setup it will not work.

A flag is just a glonal or static boolean that is initialised to false. The first thing in loop is a test to see if the flag is still false, and if it is, the existing code is run. When your sequence is complete, set the flag true.

That's a really clever way to do it, and I will experiment with it. But is it also useful in limit switch type applications? or should i scrap that Idea if i'm trying to use a limit switch?

I'm also thinking a switch case might be the best way to go about this, although I've never used one before. Any pointers on using switch cases in a situation like this?

So I am building a car for a robotics competition (here is a render, still waiting for the aluminum frame to arrive)

|500x337

The car is supposed to pass through a air of cans and then continue straight untill it reaches a certain distance, decided by the event supervisor at the competition.

While I am fully capable at the building and designing side of robotics, I am woefully inept at programming. essentially what I want to code is to have the robot wait for a button press, carry out this block of code for passing through the cans once, (don't worry, the time intervals are defined in an array, and these commands are from the adafruit motor shield library.:

    delay(500);                                          // Wait .5 seconds
    myMotor->run(FORWARD);                               // Turn dc motor on
    myMotor->setSpeed(100);                              // set dc motor speed to moderate pace
    delay(200);                                          // Wait .2 seconds
    myMotor->setSpeed(200);                              // set dc motor speed to full speed
    if ((unsigned long)(currentMillis) >= interval[1]) { // check if "interval" time has passed (200 milliseconds)
      myStepper->step(20, FORWARD, SINGLE);              // runs stepper motor 10 degrees forward
    }   
    if ((unsigned long)(currentMillis) >= interval[2]) { // check if "interval" time has passed (400 milliseconds)
      myStepper->step(40, BACKWARD, SINGLE);             // runs stepper motor 10 degrees backward
    }       
    if ((unsigned long)(currentMillis) >= interval[3]) { // check if "interval" time has passed (600 milliseconds)
      myStepper->step(20, FORWARD, SINGLE);              // runs stepper motor 10 degrees FORWARD, returning to zero
    }          
    if ((unsigned long)(currentMillis) >= interval[4]) { // check if "interval" time has passed (1000 milliseconds)
      myStepper->step(20, BACKWARD, SINGLE);             // runs stepper motor 10 degrees BACKWARD
    }              
    if ((unsigned long)(currentMillis) >= interval[5]) { // check if "interval" time has passed (1200 milliseconds)
      myStepper->step(40, FORWARD, SINGLE);              // runs stepper motor 10 degrees forward
    }
    if ((unsigned long)(currentMillis) >= interval[6]) { // check if "interval" time has passed (1400 milliseconds)
      myStepper->step(20, BACKWARD, SINGLE);             // runs stepper motor 10 degrees backward, returning to zero
    }                      
    if ((unsigned long)(currentMillis) >= interval[7]) { // check if "interval" time has passed (2000 milliseconds)
      myMotor->setSpeed(85);                             // set dc motor speed to at a much slower rate

and then I want it to keep the motor on untill it senses an end stop switch being triggered (The back axle of the car doubles as a lead screw), and then it will simply run this line of code to shut off the motors:

myMotor->run(RELEASE); // Turn dc motor off

Any help with the functions I could use to accomplish this would be great!

I attempted some code here but it doesn’t seem to work properly:
(this code is assuming the start switch and end stop are wired in line to the same digital pin for simplicity, I might try separate switches if it is easier)

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_MS_PWMServoDriver.h"

unsigned long interval[] = {200,400,600,1000,1200,1400,1600,62000,62000};     // the time we need to wait

// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 

// Connect a stepper motor with 200 steps per revolution (1.8 degree)
// to motor port #2 (M3 and M4)
Adafruit_StepperMotor *myStepper = AFMS.getStepper(200, 1);

Adafruit_DCMotor *myMotor = AFMS.getMotor(3);            // And connect a DC motor to port M1

int switchPin = 3;                                       // switch is connected to pin 2
int val;                                                 // variable for reading the pin status

void setup() {
  // put your setup code here, to run once:
  AFMS.begin();                                          // create with the default frequency 1.6KHz
  //AFMS.begin(1000);                                    // OR with a different frequency, say 1KHz
  myStepper->setSpeed(20);                              // set default stepper motor speed to 100 rpm  
}

void loop() {
  // put your main code here, to run repeatedly:
  unsigned long currentMillis = millis(); // grab current time
  val=digitalRead(switchPin);                            // read input value and store it in val
  if (val == LOW) {                                      // check if the button is pressed
    delay(500);                                          // Wait .5 seconds
    myMotor->run(FORWARD);                               // Turn dc motor on
    myMotor->setSpeed(100);                              // set dc motor speed to moderate pace
    delay(200);                                          // Wait .2 seconds
    myMotor->setSpeed(200);                              // set dc motor speed to full speed
    if ((unsigned long)(currentMillis) >= interval[1]) { // check if "interval" time has passed (200 milliseconds)
      myStepper->step(20, FORWARD, SINGLE);              // runs stepper motor 10 degrees forward
    }   
    if ((unsigned long)(currentMillis) >= interval[2]) { // check if "interval" time has passed (400 milliseconds)
      myStepper->step(40, BACKWARD, SINGLE);             // runs stepper motor 10 degrees backward
    }       
    if ((unsigned long)(currentMillis) >= interval[3]) { // check if "interval" time has passed (600 milliseconds)
      myStepper->step(20, FORWARD, SINGLE);              // runs stepper motor 10 degrees FORWARD, returning to zero
    }          
    if ((unsigned long)(currentMillis) >= interval[4]) { // check if "interval" time has passed (1000 milliseconds)
      myStepper->step(20, BACKWARD, SINGLE);             // runs stepper motor 10 degrees BACKWARD
    }              
    if ((unsigned long)(currentMillis) >= interval[5]) { // check if "interval" time has passed (1200 milliseconds)
      myStepper->step(40, FORWARD, SINGLE);              // runs stepper motor 10 degrees forward
    }
    if ((unsigned long)(currentMillis) >= interval[6]) { // check if "interval" time has passed (1400 milliseconds)
      myStepper->step(20, BACKWARD, SINGLE);             // runs stepper motor 10 degrees backward, returning to zero
    }                      
    if ((unsigned long)(currentMillis) >= interval[7]) { // check if "interval" time has passed (2000 milliseconds)
      myMotor->setSpeed(85);                             // set dc motor speed to at a much slower rate
    }
    if ((unsigned long)(currentMillis) >= interval[8]) { // check if "interval" time has passed (62000 milliseconds)     
      myMotor->run(RELEASE);                             // Turn dc motor off
    }
  }
  if (val == HIGH) {                                     // check if the button is not pressed
    myMotor->run(RELEASE);                               // Turn dc motor off
  }
}

it doesn't seem to work properly:

What does it do as opposed to what it is supposed to do ?

the motor will keep running and ignore the end stop switch

I am not familiar with the AFMS library. Does it block until each move completes? From the way the program is written I suspect it does. Also you are using blocking delay()s so your robot cannot respond to a switch until whatever is happening completes.

Have a look at how millis() is used to manage timing without blocking in several things at a time

And the AccelStepper library can control the motors without blocking.

…R

How is the switch wired ?

Your logic is completely wrong. You need to determine how much time has passed since the current step started and not do a comparison with the current value of millis(), which will quickly be larger than the intervals.

Save the millis() value when a step starts then each time through loop() compare the current value with the start value. If it is larger than the interval then move on.

@UKHeliBob I honestly just wired the switch directly into digital pin 2 wich I now realize is a mistake, but am not sure on a better way.

I implemented your suggestion on improving the function like so:

void loop() {
  val=digitalRead(switchPin);               // read input value and store it in val
  if (val == LOW) {                         // check if the button is pressed
    unsigned long currentMillis = millis(); // grab current time
    
    delay(500);              // Wait .5 seconds
    myMotor->run(FORWARD);   // Turn dc motor on
    myMotor->setSpeed(100);  // set dc motor speed to moderate pace
    delay(200);              // Wait .2 seconds
    myMotor->setSpeed(200);  // set dc motor speed to full speed
    
    if ((unsigned long)(currentMillis) >= interval[1]) { // check if "interval" time has passed (200 milliseconds)
      myStepper->step(20, FORWARD, SINGLE);   // runs stepper motor 10 degrees forward
    }   
    
    unsigned long intervalmillis = millis();  // grab current time
    if ((unsigned long)(currentMillis - interavalmillis) >= interval[2]) { // check if "interval" time has passed (400 milliseconds)
      myStepper->step(40, BACKWARD, SINGLE);  // runs stepper motor 10 degrees backward
    }       
    
    unsigned long intervalmillis = millis();  // grab current time
    if ((unsigned long)(currentMillis - interavalmillis) >= interval[3]) { // check if "interval" time has passed (600 milliseconds)
      myStepper->step(20, FORWARD, SINGLE);  // runs stepper motor 10 degrees FORWARD, returning to zero
    }          
    
    unsigned long intervalmillis = millis();  // grab current time
    if ((unsigned long)(currentMillis - interavalmillis) >= interval[4]) { // check if "interval" time has passed (1000 milliseconds)
      myStepper->step(20, BACKWARD, SINGLE); // runs stepper motor 10 degrees BACKWARD
    }              
    
    unsigned long intervalmillis = millis();  // grab current time
    if ((unsigned long)(currentMillis - interavalmillis) >= interval[5]) { // check if "interval" time has passed (1200 milliseconds)
      myStepper->step(40, FORWARD, SINGLE);  // runs stepper motor 10 degrees forward
    }
    
    unsigned long intervalmillis = millis();  // grab current time
    if ((unsigned long)(currentMillis - interavalmillis) >= interval[6]) { // check if "interval" time has passed (1400 milliseconds)
      myStepper->step(20, BACKWARD, SINGLE); // runs stepper motor 10 degrees backward, returning to zero
    }                      
    
    unsigned long intervalmillis = millis();  // grab current time
    if ((unsigned long)(currentMillis - interavalmillis) >= interval[7]) { // check if "interval" time has passed (2000 milliseconds)
      myMotor->setSpeed(85); // set dc motor speed to at a much slower rate
    }
    
    unsigned long intervalmillis = millis();  // grab current time
    if ((unsigned long)(currentMillis - interavalmillis) >= interval[8]) { // check if "interval" time has passed (62000 milliseconds)     
      myMotor->run(RELEASE); // Turn dc motor off
    }
  }
  if (val == HIGH) {                                     // check if the button is not pressed
    myMotor->run(RELEASE);                               // Turn dc motor off
  }
}

I have not had time to test it yet but I will updated this post if it works or not. Also I am still clueless If the logic I am using for the switch is right. any pointers on that speificly?

@Robin2 I do Think The AMFS library does block multi tasking. however it makes the electronics of this project so much easier so I am going to stick with It.

After Reading your example, The way you structure your code really intrigues me. would It work to create a function like "void preprogramedpath" and "void enstop" and void "braking" (I want to add code for electronic braking) and call them in the loop some way like this pseudo-code?

grab current millis; check switch; if on { check boolean flag; // so program runs once dellay 500 preprogramed path () set boolean flag on; check endstop if on{ braking () } }

if off { do nothing }

also what is a blocking delay() ?

A blocking delay is like the delay() or delayMicroseconds() functions, that busy-wait until the the required amount of time has elapsed. They block because they prevent the processor from doing anything else except service interrupts. They should be avoided, because the cycles they waste could be put to better use.

Oh yeah, the delay function, I thought a blocking delay might be something different. I understand it, but I tried to avoided it by using the millis() command more.

james_d: @Robin2 I do Think The AMFS library does block multi tasking. however it makes the electronics of this project so much easier so I am going to stick with It.

Blocking stepper motor moves are just as much trouble as the use of the delay() function.

One way to work-around the blocking moves without ditching the library is to make every move a single step. That is not nearly as impractical as it may sound at first.

...R