Using Arrays to Drive Motors at specific Intervals

Hello,

I am new to Arduino and we have a Halloween Project where my Metro with the Motor Shield is going to be driving three separate motors to turn some skeleton heads back and forth so it looks like they are looking around. My goal was to make a 'smart' program where I can just use an array to tell the motor when to start, stop and what direction to move. The code below works when I have only one motor coded, but as soon as I add a second, the first motor sometimes won't spin more than a couple of seconds and after I add the third, it really starts screwing up and the motors seem to have a mind of their own. I have a one second delay at the end of the loop so that the head movement can be counted in seconds. Does anyone know if there is a better way to program this? Here is what the end result needs to be:

Configure Motor 1 to run forward on second 2, stop on second 7, move backwards on second 10, stop on second 15, move forwards on second 16 and stop on second 19.
Something similar for the other motors.

Any help you can provide will be greatly appreciated.

Here is my code below:

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

Adafruit_MotorShield AFMS = Adafruit_MotorShield();

Adafruit_DCMotor *mtrHead1 = AFMS.getMotor(1);
Adafruit_DCMotor *mtrHead2 = AFMS.getMotor(2);
Adafruit_DCMotor *mtrHead3 = AFMS.getMotor(3);

int timer = 1000;       // Set to 1 second
int counter = 0;    // Initialize counter
int cycles = 20;    // Number of cycles (seconds) before starting over
int head1act[] = {2, 7, 10, 15, 16, 19}; // The second that an action will activate on Motor 1
int head1mv[] = {FORWARD, RELEASE, BACKWARD, RELEASE, FORWARD, RELEASE}; // The action the motor will take at said second
int head1spd[] = {255, 0, 255, 0, 255, 0}; // The speed the motor will do the action
int head2act[] = {3, 5, 8, 13, 14, 17}; // The second that an action will activate on Motor 2
int head2mv[] = {FORWARD, RELEASE, BACKWARD, RELEASE, FORWARD, RELEASE}; // The action the motor will take at said second
int head2spd[] = {255, 0, 255, 0, 255, 0}; // The speed the motor will do the action
int head3act[] = {1, 10, 12, 14, 16, 18}; // The second that an action will activate on Motor 3
int head3mv[] = {BACKWARD, RELEASE, FORWARD, RELEASE, FORWARD, RELEASE}; // The action the motor will take at said second
int head3spd[] = {255, 0, 255, 0, 255, 0}; // The speed the motor will do the action

void setup() {
  AFMS.begin();
}

void loop() {
  // loop from the lowest pin to the highest:
  for (int counter = 0; counter < cycles; counter++){
    for (int tmp = 0; tmp < sizeof(head1act); tmp++){ //check to see if the counter matches a number in motor 1 array
      if (head1act[tmp] == counter) { //If the number matches, do the action below  for motor 1
        mtrHead1->setSpeed(head1spd[tmp]); //Set motor Speed
        mtrHead1->run(head1mv[tmp]); //Run motor action
        x = sizeof(head1act); //stop going through the array
      }
    }
    for (int tmp = 0; tmp < sizeof(head2act); tmp++) {//check to see if the counter matches a number in motor 2 array 
      if (head2act[tmp] == counter) { If the number matches, do the action below  for motor 2
        mtrHead2->setSpeed(head2spd[tmp]); //Set motor Speed
        mtrHead2->run(head2mv[tmp]); //Run motor action
        x = sizeof(head2act); //stop going through the array
      }
    }
    for (int tmp = 0; tmp < sizeof(head3act); tmp++){ //check to see if the counter matches a number in motor 3 array
      if (head3act[tmp] == counter) { If the number matches, do the action below  for motor 3
        mtrHead3->setSpeed(head3spd[tmp]); //Set motor Speed
        mtrHead3->run(head3mv[tmp]); //Run motor action
        x = sizeof(head3act); //stop going through the array
      }
    }
    delay(timer);
  }
}

Welcome to the forums. Please read the sticky post at the top of the forum about how to post. Using code tags makes it easier for people to help you.

The loop() function does what the name implies - loops, so let it do that and get rid of your for() loop. check out the Several things at a time to see a better way to more independently manage each motor.

blh64:
Welcome to the forums. Please read the sticky post at the top of the forum about how to post. Using code tags makes it easier for people to help you.

The loop() function does what the name implies - loops, so let it do that and get rid of your for() loop. check out the Several things at a time to see a better way to more independently manage each motor.

Thank you the the help with the code tags. I have edited the post so that the code shows correctly. Regarding the loop, I use the for() loop to keep track of the tmp variable so I know what second the program is on. (Putting a 1000 ms delay means that each for/loop takes at least 1 second). This way, I can query the array to see if the 'second' exists. For example, at the fifth loop, it the tmp counter will be at 5 so I then query the array and see if there is a 5, if there is one, then that means the motor needs to do something at that moment and that is the reason I have the for() loop.

    for (int tmp = 0; tmp < sizeof(head1act); tmp++)  //check to see if the counter matches a number in motor 1 array

head1act is declared as an array of ints
an int takes 2 bytes
sizeof() gives the number of bytes used by the array, not the number of elements in the array

To get the number of elements in the array use

sizeof(head1act) / sizeof(head1act[0])

I suggest that you put the result in a variable so that you can print it before using it so that you can check its value

UKHeliBob:

    for (int tmp = 0; tmp < sizeof(head1act); tmp++)  //check to see if the counter matches a number in motor 1 array

head1act is declared as an array of ints
an int takes 2 bytes
sizeof() gives the number of bytes used by the array, not the number of elements in the array

To get the number of elements in the array use

sizeof(head1act) / sizeof(head1act[0])

I suggest that you put the result in a variable so that you can print it before using it so that you can check its value

Thank you very much for your suggestion. I will take a look at that and see if somehow that is causing the problem. However, when I only have one motor running, the values work and the motor works as expected, however, when I put in the second and then third motor, it just goes haywire. Also, I do not have a compiler/debugger to run the code against to try and print the value. Do you have one that you could suggest?

I do not have a compiler/debugger to run the code against to try and print the value.

You have the Arduino IDE and the Serial.print() function

UKHeliBob:

    for (int tmp = 0; tmp < sizeof(head1act); tmp++)  //check to see if the counter matches a number in motor 1 array

head1act is declared as an array of ints
an int takes 2 bytes
sizeof() gives the number of bytes used by the array, not the number of elements in the array

To get the number of elements in the array use

sizeof(head1act) / sizeof(head1act[0])

I suggest that you put the result in a variable so that you can print it before using it so that you can check its value

I just added the "/sizeof(head?act[0]" to the code and the motors are behaving exactly as I program them!
Thank you very much for that. I did not know I needed the extra code for an array of integers.

Greatly appreciate the help!

I did not know I needed the extra code for an array of integers.

The same calculation will work for any data type and it is a good idea to do it even for an array of bytes or chars

Here's an example of how to get rid of your for() loops. You just keep track of elapsed time through loop() and when a new second comes around, you then do your next action

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

Adafruit_MotorShield AFMS = Adafruit_MotorShield();

Adafruit_DCMotor *mtrHead1 = AFMS.getMotor(1);
Adafruit_DCMotor *mtrHead2 = AFMS.getMotor(2);
Adafruit_DCMotor *mtrHead3 = AFMS.getMotor(3);

int head1Idx, head2Idx, head3Idx;
const int head1act[] = {2, 7, 10, 15, 16, 19}; // The second that an action will activate on Motor 1
const int head1mv[] = {FORWARD, RELEASE, BACKWARD, RELEASE, FORWARD, RELEASE}; // The action the motor will take at said second
const int head1spd[] = {255, 0, 255, 0, 255, 0}; // The speed the motor will do the action
const int head1count = sizeof(head1act) / sizeof(head1act[0]);
const int head2act[] = {3, 5, 8, 13, 14, 17}; // The second that an action will activate on Motor 2
const int head2mv[] = {FORWARD, RELEASE, BACKWARD, RELEASE, FORWARD, RELEASE}; // The action the motor will take at said second
const int head2spd[] = {255, 0, 255, 0, 255, 0}; // The speed the motor will do the action
const int head2count = sizeof(head2act) / sizeof(head2act[0]);
const int head3act[] = {1, 10, 12, 14, 16, 18}; // The second that an action will activate on Motor 3
const int head3mv[] = {BACKWARD, RELEASE, FORWARD, RELEASE, FORWARD, RELEASE}; // The action the motor will take at said second
const int head3spd[] = {255, 0, 255, 0, 255, 0}; // The speed the motor will do the action
const int head3count = sizeof(head3act) / sizeof(head3act[0]);

unsigned long startTime;
unsigned long previousTime;
unsigned long elapsedSeconds;

void setup() {
  AFMS.begin();
  previousTime = millis() / 1000;
  startTime = previousTime;
}

void loop() {

  unsigned long currentTime = millis() / 1000;

  if ( currentTime != previousTime ) {
    // next second has elapsed
    // check for next possible movement
    previousTime = currentTime;
    elapsedSeconds = currentTime - startTime;
    if (head1act[head1Idx] == elapsedSeconds) {
      //If the number matches, do the action below  for motor 1
      mtrHead1->setSpeed(head1spd[head1Idx]); //Set motor Speed
      mtrHead1->run(head1mv[head1Idx]); //Run motor action
      head1Idx = (head1Idx + 1) % head1count;
    }
    if (head2act[head2Idx] == elapsedSeconds) {
      //If the number matches, do the action below  for motor 2
      mtrHead2->setSpeed(head2spd[head2Idx]); //Set motor Speed
      mtrHead2->run(head2mv[head2Idx]); //Run motor action
      head2Idx = (head2Idx + 1) % head2count;
    }
    if (head3act[head3Idx] == elapsedSeconds) {
      //If the number matches, do the action below  for motor 3
      mtrHead3->setSpeed(head3spd[head3Idx]); //Set motor Speed
      mtrHead3->run(head3mv[head3Idx]); //Run motor action
      head3Idx = (head3Idx + 1) % head3count;
    }
  } else {
    // seconds have not incremented
    // but you could put any code here to
    // check buttons, etc. in the future
    
  }
}

Congratulations on getting that running.
Now explore and understand everything it does!

It’s quite complicated code for a beginner, so there’s a lot to understand.
Good luck.