Timer and inclusion of multiple switches Train/Trolley Mode MotorSheild V2

HI,
I'm using the Adafruit Motor Shield V3.

Been through a bunch of tutorials but just can't figure it out.

I have 4 trains connected to the board identified as Train1, Train2, Train3 and Train4.
Each train has a direction switch. DirectionSW1, DirectionSW2, DirectionSW3, and DirectionSW4
Each Train has a POT to control Speed. Speed1, Speed2, Speed3, Speed4

Currently I have an array setup to handle Train1-3. This part works.

For Train4, I have to set it up so it has 2 addition switches. 1 to flip it to Trolley Mode or Train Mode. the other to set the delay for 30-60 seconds.

In Train Mode, it needs to operate the same as the other 3 trains.

When in Trolley Mode, it has to run for a set period of time... Say 30 Seconds then wait for a set period of time (30 or 60 seconds) then reverse direction.

In Trolley Mode the POT for Speed4 needs to control speed as well.

I'm lost. I have tried the mills function but I can never seem to figure out how to make the program work.

My code just keeps getting out of hand and never works for the trolley/train mode switch with the timers.

Not sure how to structure the code.

#include <Wire.h>
#include <Adafruit_MotorShield.h>

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

// Select which 'port' M1, M2, M3 or M4. This controls the Trains attached to the ports defined.
Adafruit_DCMotor *Train1 = AFMS.getMotor(1);
Adafruit_DCMotor *Train2 = AFMS.getMotor(2);
Adafruit_DCMotor *Train3 = AFMS.getMotor(3);
Adafruit_DCMotor *Train4 = AFMS.getMotor(4);


// Train Motor Array
Adafruit_DCMotor *trains[4] = {Train1, Train2, Train3, Train4};

//Define POT Speed controls
int trainSpeeds[4] = {A0, A1, A2, A3};

// Train state array
int trainStates[4];

//Define Switches that control Direction of Trains
int DirectionSW1 = 2;  // Direction Switch for Train 1. Connected to Digital Input 1
int DirectionSW2 = 3; // Direction Switch for Train 2. Connected to Digital Input 2
int DirectionSW3 = 4; // Direction Switch for Train 3. Connected to Digital Input 3
int DirectionSW4 = 5; // Direction Switch for Train 4. Connected to Digital Input 4

int switches[4] = { DirectionSW1, DirectionSW2, DirectionSW3, DirectionSW4};

// Define switches related to Trolleys
int TrolleyModeSwitch = 6;   // TRUE=Trolley mode, FALSE=Train mode
int TrolleyDelaySwitch = 7;  // TRUE=5 second delay, FALSE=10 second delay

//

// Setup Board and PinMode 
void setup() {
  Serial.begin(9600);           // set up Serial library at 9600 bps

  //Setup internal PULLUP for trains
  pinMode(DirectionSW1,INPUT_PULLUP); // Direction1 Switch
  pinMode(DirectionSW2,INPUT_PULLUP); // Direction2 Switch
  pinMode(DirectionSW3,INPUT_PULLUP); // Direction3 Switch
  pinMode(DirectionSW4,INPUT_PULLUP); // Direction4 Switch

  // PULLUP mode for Trolley Switches
  pinMode(TrolleyModeSwitch ,INPUT_PULLUP); // TrainTrolleyMode Switch 
  pinMode(TrolleyDelaySwitch,INPUT_PULLUP); // TrolleyWaitDelay Switch

  AFMS.begin();  // create with the default frequency 1.6KHz
  //AFMS.begin(1000);  // OR with a different frequency, say 1KHz
}
void loop() {
  // Loop through trains (trainArray starts at 0)
  for (int trainIndex=0; trainIndex<4; trainIndex++) {
    trainStates[trainIndex] = digitalRead(switches[trainIndex]);
    if (trainStates[trainIndex] == LOW) {
      trains[trainIndex]-> run(FORWARD);
    } else {
      trains[trainIndex]-> run(BACKWARD);
    }
    trains[trainIndex] -> setSpeed(analogRead(trainSpeeds[trainIndex])/4);
  }


}

attached is a frizzing diagram and the code that works for all 4 trains in the array. Again, I need to pull the 4th out of array and add the train/trolley switch with delay function.

Thanks in advance for the help, just really need the help of someone that knows more than I do.

TC_4Trains_in_Array.ino (2.35 KB)

Adafruit_MotorShield AFMS = Adafruit_MotorShield();

I really can't figure out why Adafruit keeps creating examples with crap like this in it. One never calls a constructor directly.

Adafruit_MotorShield AFMS;

is the proper way to create an instance of the class when the constructor takes no arguments.

//Define POT Speed controls
int trainSpeeds[4] = {A0, A1, A2, A3};

That array does NOT contain speeds. Use a name that reflects what the array contains.

int DirectionSW1 = 2;  // Direction Switch for Train 1. Connected to Digital Input 1
int DirectionSW2 = 3; // Direction Switch for Train 2. Connected to Digital Input 2
int DirectionSW3 = 4; // Direction Switch for Train 3. Connected to Digital Input 3
int DirectionSW4 = 5; // Direction Switch for Train 4. Connected to Digital Input 4

If you are going to have useless comments, you MUST make the code match the useless comments.

Your code currently treats all 4 trains exactly the same way. Why?

I'm lost. I have tried the mills function but I can never seem to figure out how to make the program work.

You won't be able to use millis() in a for loop.
You need a state machine instead (see the switch - case construct).
You will need the following states:

  1. set up train 1
  2. set up train 2
  3. set up train 3
  4. If not in trolley mode, set up train 4 and go to state 1, else state 5
  5. start trolley for desired time, if time not up go to state 1, else state 6
  6. stop trolley for desired time. if time not up go to state 1, else reset timers.
    go to state 1.

My first step would be to take the direct setting of speed and direction out of the loop and place it in a separate function; I called it controlTrain.

/*
  set trainspeed and direction for train specified by index
*/
void controlTrain(int index, int direction, int speed)
{
  // set direction
  if (direction == LOW)
    trains[index]->run(FORWARD);
  else
    trains[index]->run(BACKWARD);
  // set speed
  trains[index]->setSpeed(speed);
}

In your for block, you can call it like

  // Loop through trains (trainArray starts at 0)
  for (int trainIndex = 0; trainIndex < 4; trainIndex++)
  {
    // read the speed from the potentiometer (and divide by 4)
    int speed = analogRead(trainSpeeds[trainIndex]) / 4;
    // get the direction from the switch
    int direction = digitalRead(switches[trainIndex]);

    // control the train
    controlTrain(trainIndex, direction, speed);
  }

Test this first (I can't).

Next you can write a delay function like below that implements a non-blocking delay

/*
  delay for number of milliseconds
  returns false if delay in progress, else true
*/
bool doDelay(unsigned long duration)
{
  // remember the start time of the delay; 0 means that delay is not started
  static unsigned long starttime = 0;
  if (starttime == 0)
  {
    // set the start time
    starttime = millis();
  }
  else
  {
    // if delay lapsed
    if (millis() - starttime > duration)
    {
      // reset start time
      starttime = 0;
      // indicate that delay has lapsed
      return true;
    }
  }
  // indicate delay is in progress
  return false;
}

Lastly implement a function that is used in trolley mode; it uses a simple state machine (as mentioned above)

/*
  statemachine for trolley mode
*/
void doTrolley(int index, int speed)
{
  // remember what trolley is doing
  static int trolleystate = 0;
  // remember trolley direction
  static int trolleydirection = LOW;

  switch (trolleystate)
  {
    case 0:
      // run for 30 seconds
      controlTrain(index, trolleydirection, speed);
      if (doDelay(30000))
      {
        // done with this state
        trolleystate++;
      }
      break;
    case 1:
      // stop for 60 seconds
      controlTrain(index, trolleydirection, 0);
      if (doDelay(60000))
      {
        // done with this state
        trolleystate++;
      }
      break;
    case 2:
      // change direction
      if (trolleydirection == LOW)
      {
        trolleydirection = HIGH;
      }
      else
      {
        trolleydirection = LOW;
      }
      // done with this state, back to first state
      trolleystate = 0;
      break;
  }
}

Lastly implement some logic in the for loop to determine what to do

// define sensible name for trolley mode
#define TROLLEYMODE LOW


void loop()
{
  // Loop through trains (trainArray starts at 0)
  for (int trainIndex = 0; trainIndex < 4; trainIndex++)
  {
    // read the speed from the potentiometer (and divide by 4)
    int speed = analogRead(trainSpeeds[trainIndex]) / 4;
    // get the direction from the switch
    int direction = digitalRead(switches[trainIndex]);


    if (trainIndex < 3 || (trainIndex == 3 && digitalRead(TrolleyModeSwitch) != TROLLEYMODE))
    {
      controlTrain(trainIndex, direction, speed);
    }
    else
    {
      doTrolley(trainIndex, speed);
    }
  }
}

I hope this gives you a framework to work with. Please note that doTrolley() remembers its last state and direction so switching from trolley mode to train mode to trolley mode might give some surprises.

//Edit: fixed some typos

Sterretje--

Should I start your mplementation by just pasting all of your sections together?

Don't want to go down another rabbit hole..

Thanks again

I tried to implement the blocks Sterretje suggested.

I get compile errors.

Am I even going the right direction trying to implement this code

current error message is

Arduino: 1.6.7 (Mac OS X), Board: "Arduino/Genuino Uno"

/Users/test/Documents/Arduino/TC_4Trains_in_Array_TrollyChanges/TC_4Trains_in_Array_TrollyChanges.ino: In function 'void loop()':
TC_4Trains_in_Array_TrollyChanges:76: error: a function-definition is not allowed here before '{' token
 {
 ^
TC_4Trains_in_Array_TrollyChanges:99: error: 'controlTrain' was not declared in this scope
     controlTrain(trainIndex, direction, speed);
                                              ^
TC_4Trains_in_Array_TrollyChanges:116: error: 'controlTrain' was not declared in this scope
     controlTrain(trainIndex, direction, speed);
                                              ^
TC_4Trains_in_Array_TrollyChanges:129: error: a function-definition is not allowed here before '{' token
 {
 ^
TC_4Trains_in_Array_TrollyChanges:233: error: expected '}' at end of input
 }
 ^
exit status 1
a function-definition is not allowed here before '{' token

  This report would have more information with
  "Show verbose output during compilation"
  enabled in File > Preferences.

current code below

#include <Wire.h>
#include <Adafruit_MotorShield.h>

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

// Select which 'port' M1, M2, M3 or M4. This controls the Trains attached to the ports defined.
Adafruit_DCMotor *Train1 = AFMS.getMotor(1);
Adafruit_DCMotor *Train2 = AFMS.getMotor(2);
Adafruit_DCMotor *Train3 = AFMS.getMotor(3);
Adafruit_DCMotor *Train4 = AFMS.getMotor(4);


// Train Motor Array
Adafruit_DCMotor *trains[4] = {Train1, Train2, Train3, Train4};

//Define POT Speed controls
int trainSpeeds[4] = {A0, A1, A2, A3};

// Train state array
int trainStates[4];

//Define Switches that control Direction of Trains
int DirectionSW1 = 2;  // Direction Switch for Train 1. Connected to Digital Input 2
int DirectionSW2 = 3; // Direction Switch for Train 2. Connected to Digital Input 3
int DirectionSW3 = 4; // Direction Switch for Train 3. Connected to Digital Input 4
int DirectionSW4 = 5; // Direction Switch for Train 4. Connected to Digital Input 5

int switches[4] = { DirectionSW1, DirectionSW2, DirectionSW3, DirectionSW4};

// Define switches related to Trolleys
int TrolleyModeSwitch = 6;   // TRUE=Trolley mode, FALSE=Train mode
int TrolleyDelaySwitch = 7;  // TRUE=5 second delay, FALSE=10 second delay

//

// Setup Board and PinMode 
void setup() {
  Serial.begin(9600);           // set up Serial library at 9600 bps

  //Setup internal PULLUP for trains
  pinMode(DirectionSW1,INPUT_PULLUP); // Direction1 Switch
  pinMode(DirectionSW2,INPUT_PULLUP); // Direction2 Switch
  pinMode(DirectionSW3,INPUT_PULLUP); // Direction3 Switch
  pinMode(DirectionSW4,INPUT_PULLUP); // Direction4 Switch

  // PULLUP mode for Trolley Switches
  pinMode(TrolleyModeSwitch ,INPUT_PULLUP); // TrainTrolleyMode Switch 
  pinMode(TrolleyDelaySwitch,INPUT_PULLUP); // TrolleyWaitDelay Switch

  AFMS.begin();  // create with the default frequency 1.6KHz
  //AFMS.begin(1000);  // OR with a different frequency, say 1KHz
}
void loop() {
  
  ///////old code
  // Loop through trains (trainArray starts at 0)
  // for (int trainIndex=0; trainIndex<4; trainIndex++) {
 //   trainStates[trainIndex] = digitalRead(switches[trainIndex]);
 //   if (trainStates[trainIndex] == LOW) {
 //     trains[trainIndex]-> run(FORWARD);
//    } else {
//      trains[trainIndex]-> run(BACKWARD);
//    }
//    trains[trainIndex] -> setSpeed(analogRead(trainSpeeds[trainIndex])/4);
//  }
//////end old code

///begin Ster changes
///block1

/*
  set trainspeed and direction for train specified by index
*/
void controlTrain(int index, int direction, int speed)
{
  // set direction
  if (direction == LOW)
    trains[index]->run(FORWARD);
  else
    trains[index]->run(BACKWARD);
  // set speed
  trains[index]->setSpeed(speed);
}
//block1 end

//Block2 begin


  // Loop through trains (trainArray starts at 0)
  for (int trainIndex = 0; trainIndex < 4; trainIndex++)
  {
    // read the speed from the potentiometer (and divide by 4)
    int speed = analogRead(trainSpeeds[trainIndex]) / 4;
    // get the direction from the switch
    int direction = digitalRead(switches[trainIndex]);

    // control the train
    controlTrain(trainIndex, direction, speed);
  }

  //end Block2

  //Block3

  
  // Loop through trains (trainArray starts at 0)
  for (int trainIndex = 0; trainIndex < 4; trainIndex++)
  {
    // read the speed from the potentiometer (and divide by 4)
    int speed = analogRead(trainSpeeds[trainIndex]) / 4;
    // get the direction from the switch
    int direction = digitalRead(switches[trainIndex]);

    // control the train
    controlTrain(trainIndex, direction, speed);
  }

  //end block3

  //begin Block4

  
/*
  delay for number of milliseconds
  returns false if delay in progress, else true
*/
bool doDelay(unsigned long duration)
{
  // remember the start time of the delay; 0 means that delay is not started
  static unsigned long starttime = 0;
  if (starttime == 0)
  {
    // set the start time
    starttime = millis();
  }
  else
  {
    // if delay lapsed
    if (millis() - starttime > duration)
    {
      // reset start time
      starttime = 0;
      // indicate that delay has lapsed
      return true;
    }
  }
  // indicate delay is in progress
  return false;
}
//End Block4

//begin Block5

/*
  statemachine for trolley mode
*/
void doTrolley(int index, int speed)
{
  // remember what trolley is doing
  static int trolleystate = 0;
  // remember trolley direction
  static int trolleydirection = LOW;

  switch (trolleystate)
  {
    case 0:
      // run for 30 seconds
      controlTrain(index, trolleydirection, speed);
      if (doDelay(30000))
      {
        // done with this state
        trolleystate++;
      }
      break;
    case 1:
      // stop for 60 seconds
      controlTrain(index, trolleydirection, 0);
      if (doDelay(60000))
      {
        // done with this state
        trolleystate++;
      }
      break;
    case 2:
      // change direction
      if (trolleydirection == LOW)
      {
        trolleydirection = HIGH;
      }
      else
      {
        trolleydirection = LOW;
      }
      // done with this state, back to first state
      trolleystate = 0;
      break;
  }
}

// End Block 5

//Begin Block6


// define sensible name for trolley mode
#define TROLLEYMODE LOW


void loop()
{
  // Loop through trains (trainArray starts at 0)
  for (int trainIndex = 0; trainIndex < 4; trainIndex++)
  {
    // read the speed from the potentiometer (and divide by 4)
    int speed = analogRead(trainSpeeds[trainIndex]) / 4;
    // get the direction from the switch
    int direction = digitalRead(switches[trainIndex]);


    if (trainIndex < 3 || (trainIndex == 3 && digitalRead(TrolleyModeSwitch) != TROLLEYMODE))
    {
      controlTrain(trainIndex, direction, speed);
    }
    else
    {
      doTrolley(trainIndex, speed);
    }
  }
}

//end Block 6
}

TC_4Trains_in_Array_TrollyChanges.ino (5.77 KB)

Still stuck, can someone please help by telling me what I'm doing wrong?

I appreciate any advise
Thanks

You basically pasted the controlTrain function in the middle of loop() and that is more than likely the root cause of the first error. The other errors are more than likely a result of that. You also have a second loop() function; you can only have one. Below the framework; I've placed my code in there and filled in loop(). You just need to do the setup() and the variable declarations before that.

...
...
// define sensible name for trolley mode
#define TROLLEYMODE LOW
...
...
void setup()
{
...
...
}


void loop()
{
  // Loop through trains (trainArray starts at 0)
  for (int trainIndex = 0; trainIndex < 4; trainIndex++)
  {
    // read the speed from the potentiometer (and divide by 4)
    int speed = analogRead(trainSpeeds[trainIndex]) / 4;
    // get the direction from the switch
    int direction = digitalRead(switches[trainIndex]);


    if (trainIndex < 3 || (trainIndex == 3 && digitalRead(TrolleyModeSwitch) != TROLLEYMODE))
    {
      controlTrain(trainIndex, direction, speed);
    }
    else
    {
      doTrolley(trainIndex, speed);
    }
  }
}

/*
  set trainspeed and direction for train specified by index
*/
void controlTrain(int index, int direction, int speed)
{
  // set direction
  if (direction == LOW)
    trains[index]->run(FORWARD);
  else
    trains[index]->run(BACKWARD);
  // set speed
  trains[index]->setSpeed(speed);
}

/*
  delay for number of milliseconds
  returns false if delay in progress, else true
*/
bool doDelay(unsigned long duration)
{
  // remember the start time of the delay; 0 means that delay is not started
  static unsigned long starttime = 0;
  if (starttime == 0)
  {
    // set the start time
    starttime = millis();
  }
  else
  {
    // if delay lapsed
    if (millis() - starttime > duration)
    {
      // reset start time
      starttime = 0;
      // indicate that delay has lapsed
      return true;
    }
  }
  // indicate delay is in progress
  return false;
}

/*
  statemachine for trolley mode
*/
void doTrolley(int index, int speed)
{
  // remember what trolley is doing
  static int trolleystate = 0;
  // remember trolley direction
  static int trolleydirection = LOW;

  switch (trolleystate)
  {
    case 0:
      // run for 30 seconds
      controlTrain(index, trolleydirection, speed);
      if (doDelay(30000))
      {
        // done with this state
        trolleystate++;
      }
      break;
    case 1:
      // stop for 60 seconds
      controlTrain(index, trolleydirection, 0);
      if (doDelay(60000))
      {
        // done with this state
        trolleystate++;
      }
      break;
    case 2:
      // change direction
      if (trolleydirection == LOW)
      {
        trolleydirection = HIGH;
      }
      else
      {
        trolleydirection = LOW;
      }
      // done with this state, back to first state
      trolleystate = 0;
      break;
  }
}

Thank you!!

I appreciate you advise and help, Learned a lot from you.

Question:

Do I need to read the switch TrolleyDelaySwitch (30 or 60) or is that taken care of. Not sure where I should read it.

Thanks again

Not included; you can read it at the beginning of doTrolley and set a delay duration.

/*
  statemachine for trolley mode
*/
void doTrolley(int index, int speed)
{
  // remember what trolley is doing
  static int trolleystate = 0;
  // remember trolley direction
  static int trolleydirection = LOW;

  // duration of delay
  unsigned long delayduration;
  if(digitalRead(yourPin)  == LOW)
  {
    delayduration(30000);
  }
  else
  {
    delayduration(60000);
  }
  ...
  ...

and change

      if (doDelay(60000))

to

      if (doDelay(delayduration))

Note that delayduration is read every time so if a delay is in progress and you change the setting it will immediately make the delay shorter or longer.