Running multiple Stepper motors in parallel

Hello,

I am building a type of 3D printer that uses 8 hoists, each powered by a NEMA 17 motor. The main software is written in processing but im using two arduino Megas to control the motors and various other bits (fans heaters etc).

I have written some code that takes a string sent from P3 and interprets it into steps and direction for each motor which works fine. The issue is that the motors run in series not parallel, which means the print would be ugly and slow.

is there a way to run all 8 motors in parallel so that they start their first step at the same time and their last step at the same time (each motor would run at different speed)?

I think the AccelStepper/ Multistepper library could do it but don't know what the code should look like.

the string that's received looks something like this:

letter (A-H) = which motor, first number = direction (1 = clockwise, 0 = anticlockwise), next 4 numbers are the amount of steps.

Heres the current code:

int steps[] = {23, 25, 33, 37, 41, 45, 22, 26, 32, 36, 40, 44, 48}; //motor pin number array. these pins set whether power is on or off
int dir[] = {22, 24, 31, 35, 39, 43, 24, 28, 30, 34, 38, 42, 46}; //power pin number array. These are the pins that send power to each motor.
int limitpin[] = {22, 10, 4, 12, 23, 11, 5, 9};//first four are top, second four are bottom
int limits[] = {0, 0, 0, 0, 0, 0, 0, 0}; // first four are top, second four are bottom
char limChar[] = {'Z', '0', '0', '0', '0', '0', '0', '0', '0'};
char motor[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'Z'};
boolean stopped;
boolean done = true;
boolean received[13];
boolean completed[13];
char directions[13];
int nSteps[13];
int inc = 800;
int iSteps[] = {inc, inc, inc, inc, inc, inc, inc, inc, inc, inc, inc, inc, inc};
int mStepsXY = 0;
long sT[13];
int khz = 200;
boolean nw = false; //boolean to indicate whether the shownewdata function should run
const byte MaxChar = 64; //maximum number of characters a string sent over comms can have.
char input[MaxChar]; //input array for incoming information from the comms port


void setup() {
  Serial.begin(56700); //opens the comms port - the port needs to be the same as in processing. The higher the number the faster it can communicate but increases error
  for (int z = 0; z < 13; z++) {
    pinMode(steps[z], OUTPUT); //Sets the power pins as outputs
    digitalWrite(steps[z], LOW); //sets the motor pins to start as off    if ((z > 0) { //this part is only ran for the first 8 loops as there are only 8 power pins
    pinMode(dir[z], OUTPUT); //Sets the power pins as outputs
    digitalWrite(dir[z], LOW); //sets the power pins to start as "off"
    received[z] = false;
    completed[z] = false;
    if (z > 4) {
      pinMode(limitpin[z], INPUT); //Sets the limit pins as inputs
    }
  }
}

void loop() {
  readbytes();
}

void readbytes() {
  //Defines the local variables
  static boolean InProgress = false; //boolean to indicate whether a string is being put together or not
  static byte ndx = 0; //placeholder for number of characters in the string
  char start = '<'; //this is added to the start of the string before its sent to indicate when a new input has been sent
  char finish = '>'; //this is added to the end of the string before its sent to indicate when a new input has finished being sent
  char ReadChar; //this is the current value stored in the comms port
  while (Serial.available() > 0 && nw == false) {

    ReadChar = Serial.read(); //stores the current value in the comms port for analysis
    if (InProgress == true) { //checks if we are currently putting a string together
      if (ReadChar != finish) { //if the current value in the comms port is not the finish character (i.e ">") this evaluates to true
        input[ndx] = ReadChar; //store the value in the input array
        ndx++; //move to the next character in the string
        if (ndx >= MaxChar) {
          ndx = MaxChar - 1; //if the loop has ran more than the max length of string, overwrite the last entry
        }
      }
      else {//if the current value in the comms port is the finish character (>) this evaluates to true
        input[ndx] = '\0'; //adds a line break to the input array to finish the string
        InProgress = false; //tells the program we're done putting a string together for now
        ndx = 0; //set the input array index back to 0
        nw = true; //gets ready to run the shownewdata function
        interpretdata();
        runhoists();
      }
    } else if (ReadChar == start) { //if the current value in the comms port is the start character (i.e "<") this evaluates to true
      InProgress = true; //tells the program we are currently putting a string together
    }
  }
}

void interpretdata() {
  if (input[0] == 'A') {
    for (int i = 0; i < 8; i++) {
      if (input[i * 6] == motor[i]) { //if the input char is the same as the motor indicator char...
        directions[i] = input[(i * 6) + 1]; //the next input char is recorded as the direction
        char strg[3];
        for (int l = 0; l < 4; l++) {
          strg[l] = input[(i * 6) + (l + 2)]; //the next 4 input chars are the number of steps
        }
        nSteps[i] = atoi(strg); //converts the no of steps string into an integer
        received[i] = true; //record that that hoists info has been received for the next coordinate
      }
    }
  }
  for (int z = 0; z < 52; z++) {
    input[z] = "";
  }
  Serial.flush();
  for (int x = 0; x < 8; x++) {
    if (nSteps[x] > mStepsXY) {
      mStepsXY = nSteps[x];
    }
  }
}

void runhoists() {

  if ((received[0] == true) && (received[1] == true) && (received[2] == true) && (received[3] == true)
      && (received[4] == true) && (received[5] == true) && (received[6] == true) && (received[7] == true)) {
    for (int i = 0; i < 8; i++) {
      if (directions[i] == '0') { // if direction info is 0
        digitalWrite(dir[i], HIGH); // set motor direction to anticlockwise
      }
      else if (directions[i] == '1') {
        digitalWrite(dir[i], LOW); // if its 1 set motor direction to clockwise
      }
    }
    for (int k = 0; k < ((mStepsXY/inc)+1); k++) {
      for (int i = 0; i < 8; i++) {
        for (int z = 0; z < inc; z++) {
          if ((nSteps[i] > 0 ) && (z < nSteps[i]))  {
            sT[i] = micros();
            while (micros() - sT[i] < khz) {
              digitalWrite(steps[i], HIGH);
            }
          }
          digitalWrite(steps[i], LOW);
        }
      }
    }
    sendback();
  }
}

void sendback() {
  for (int i = 0; i < 8; i++) {
    received[i] = false; // clear the received signals
    nSteps[i] = 0;
    iSteps[i] = 0;
  }
  mStepsXY = 0;
  //  t = 0;
  Serial.println("W"); // send a signal back to processing that the coord is done.
  Serial.flush();
  nw = false;
}

the string that's received looks something like this:

"<"A17791B08055C05394D12393E15326F07316G05168H12238">"

(ignore the "'s)

letter (A-H) = which motor, first number = direction (1 = clockwise, 0 = anticlockwise), next 4 numbers are the amount of steps.

So motor "A" will run clockwise for 7791 steps.

This is called "coordinated motion", and I'm not sure there's an out-of-the-box solution for 8 axis coordinated motion out there, Arduino CNC libraries typically have 3 or 4 axes only.

Perhaps someone has generalized GRBL for more axes? It would probably have
to be a version ported to faster hardware than the AVR Arduinos... I know there is
at least one CNC project out there for the Teensy, and a Teensy4 would be plenty
fast enough for anything really...

ive managed to get it running 4 in parallel. any higher and all the motors miss steps here and there but its progress. I basically just had to split the for loop between setting motors high and setting them back low into two. thought id share the code:

void runhoists() {

  for (int i = G; i < 8 + G; i++) {
    if (directions[i] == '0') { // if direction info is 0
      digitalWrite(dir[i], HIGH); // set motor direction to anticlockwise
    }
    else if (directions[i] == '1') {
      digitalWrite(dir[i], LOW); // if its 1 set motor direction to clockwise
    }
  }

  for (int z = 0; z < mStepsXY; z++) {
    for ( int j = 1; j < 2; j++) {
      for (int i = G; i < 4 + G; i++) {
        if ((nSteps[i * j] > 0 ) && (iSteps[i * j] <= nSteps[i * j])) { // && (go == true)) {
          digitalWrite(steps[(i) * j], HIGH);
          iSteps[i * j]++;
        }
      }
      delayMicroseconds(khz);
      for (int i = G; i < 4 + G; i++) {
        digitalWrite(steps[i * j], LOW);
      }
    }
  }
  sendback(1);
}

this doesn't give coordinated motion but since each coordinate is <1mm i don't think it'll matter too much.