Stepper Motor Running At Same Time Serial Arduino

Posted on

Control Stepper Motor Serial Monitor Arduino

Stepper Motor Serial Monitor Arduino Running At Same Time – Previously I have made an article about Keyes CNC Shield V4 GRBL and how to control via a4988 driver. But a problem arises when we can’t control the stepper motor to rotate at the same time.

Therefore, I tried several methods and using a timer interrupt was the right choice after learning the program code from the video Coordinated stepper motor control (arduino) and then I edited a few lines of the program and set the right interrupt to run on the Arduino Uno / Nano.

A4988 CNC Shield Stepper Motor Schematic

In this article, I still use the CNC Shield V4 a4988 as a stepper motor controller to facilitate this test. For the schematic, please look at the following picture:

Stepper Motor Serial Arduino Running At Same Time

The stepper motor has 4 wires, red blue green and black, please make sure you connect it to the CNC Shield correctly. If the connection is wrong, it could be that the stepper is spinning incorrectly. I have provided the program code for you below.

Arduino Code Motor Stepper

This is a program that I modified, you can copy and paste into your arduino IDE, then upload it to arduino.

#define X_DIR_PIN          2
#define X_STEP_PIN         5
#define X_ENABLE_PIN       8

#define Y_DIR_PIN          3
#define Y_STEP_PIN         6
#define Y_ENABLE_PIN       8

#define Z_DIR_PIN          4
#define Z_STEP_PIN         7
#define Z_ENABLE_PIN       8


#define X_STEP_HIGH             PORTD |=  (1 << X_STEP_PIN);
#define X_STEP_LOW              PORTD &= ~(1 << X_STEP_PIN);

#define Y_STEP_HIGH             PORTD |=  (1 << Y_STEP_PIN);
#define Y_STEP_LOW              PORTD &= ~(1 << Y_STEP_PIN);
//
#define Z_STEP_HIGH             PORTD |=  (1 << Z_STEP_PIN);
#define Z_STEP_LOW              PORTD &= ~(1 << Z_STEP_PIN);


#define TIMER1_INTERRUPTS_ON    TIMSK1 |=  (1 << OCIE1A);
#define TIMER1_INTERRUPTS_OFF   TIMSK1 &= ~(1 << OCIE1A);

byte a;
int x = 0;
int y = 0;
int z = 0;

String get_data[3];
String data;
int stringData;
String per_data;

struct stepperInfo {
  // externally defined parameters
  float acceleration;
  volatile unsigned int minStepInterval;   // ie. max speed, smaller is faster
  void (*dirFunc)(int);
  void (*stepFunc)();

  // derived parameters
  unsigned int c0;                // step interval for first step, determines acceleration
  long stepPosition;              // current position of stepper (total of all movements taken so far)

  // per movement variables (only changed once per movement)
  volatile int dir;                        // current direction of movement, used to keep track of position
  volatile unsigned int totalSteps;        // number of steps requested for current movement
  volatile bool movementDone = false;      // true if the current movement has been completed (used by main program to wait for completion)
  volatile unsigned int rampUpStepCount;   // number of steps taken to reach either max speed, or half-way to the goal (will be zero until this number is known)

  // per iteration variables (potentially changed every interrupt)
  volatile unsigned int n;                 // index in acceleration curve, used to calculate next interval
  volatile float d;                        // current interval length
  volatile unsigned long di;               // above variable truncated
  volatile unsigned int stepCount;         // number of steps completed in current movement
};

void xStep() {
  X_STEP_HIGH
  X_STEP_LOW
}
void xDir(int dir) {
  digitalWrite(X_DIR_PIN, dir);
}

void yStep() {
  Y_STEP_HIGH
  Y_STEP_LOW
}
void yDir(int dir) {
  digitalWrite(Y_DIR_PIN, dir);
}

void zStep() {
  Z_STEP_HIGH
  Z_STEP_LOW
}
void zDir(int dir) {
  digitalWrite(Z_DIR_PIN, dir);
}


void resetStepperInfo( stepperInfo& si ) {
  si.n = 0;
  si.d = 0;
  si.di = 0;
  si.stepCount = 0;
  si.rampUpStepCount = 0;
  si.totalSteps = 0;
  si.stepPosition = 0;
  si.movementDone = false;
}

#define NUM_STEPPERS 3

volatile stepperInfo steppers[NUM_STEPPERS];

void setup() {

  Serial.begin(9600);

  pinMode(X_STEP_PIN,   OUTPUT);
  pinMode(X_DIR_PIN,    OUTPUT);
  pinMode(X_ENABLE_PIN, OUTPUT);

  pinMode(Y_STEP_PIN,   OUTPUT);
  pinMode(Y_DIR_PIN,    OUTPUT);
  pinMode(Y_ENABLE_PIN, OUTPUT);

  pinMode(Z_STEP_PIN,   OUTPUT);
  pinMode(Z_DIR_PIN,    OUTPUT);
  pinMode(Z_ENABLE_PIN, OUTPUT);


  digitalWrite(X_ENABLE_PIN, LOW);
  digitalWrite(Y_ENABLE_PIN, LOW);
  digitalWrite(Z_ENABLE_PIN, LOW);

  noInterrupts();
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;

  OCR1A = 1000;                             // compare value
  TCCR1B |= (1 << WGM12);                   // CTC mode
  TCCR1B |= ((1 << CS11) | (1 << CS10));    // 64 prescaler
  interrupts();


  steppers[0].dirFunc = xDir;
  steppers[0].stepFunc = xStep;
  steppers[0].acceleration = 1000;
  steppers[0].minStepInterval = 250;

  steppers[1].dirFunc = yDir;
  steppers[1].stepFunc = yStep;
  steppers[1].acceleration = 1000;
  steppers[1].minStepInterval = 250;

  steppers[2].dirFunc = zDir;
  steppers[2].stepFunc = zStep;
  steppers[2].acceleration = 1000;
  steppers[2].minStepInterval = 250;
}

void resetStepper(volatile stepperInfo& si) {
  si.c0 = si.acceleration;
  si.d = si.c0;
  si.di = si.d;
  si.stepCount = 0;
  si.n = 0;
  si.rampUpStepCount = 0;
  si.movementDone = false;
}

volatile byte remainingSteppersFlag = 0;

void prepareMovement(int whichMotor, int steps) {
  volatile stepperInfo& si = steppers[whichMotor];
  si.dirFunc( steps < 0 ? HIGH : LOW );
  si.dir = steps > 0 ? 1 : -1;
  si.totalSteps = abs(steps);
  resetStepper(si);
  remainingSteppersFlag |= (1 << whichMotor);
}

volatile byte nextStepperFlag = 0;

volatile int ind = 0;
volatile unsigned int intervals[100];

void setNextInterruptInterval() {

  bool movementComplete = true;

  unsigned int mind = 999999;
  for (int i = 0; i < NUM_STEPPERS; i++) {
    if ( ((1 << i) & remainingSteppersFlag) && steppers[i].di < mind ) {
      mind = steppers[i].di;
    }
  }

  nextStepperFlag = 0;
  for (int i = 0; i < NUM_STEPPERS; i++) {
    if ( ! steppers[i].movementDone )
      movementComplete = false;

    if ( ((1 << i) & remainingSteppersFlag) && steppers[i].di == mind )
      nextStepperFlag |= (1 << i);
  }

  if ( remainingSteppersFlag == 0 ) {
    OCR1A = 65500;
  }

  OCR1A = mind;
}

ISR(TIMER1_COMPA_vect)
{
  unsigned int tmpCtr = OCR1A;

  OCR1A = 65500;

  for (int i = 0; i < NUM_STEPPERS; i++) {

    if ( ! ((1 << i) & remainingSteppersFlag) )
      continue;

    if ( ! (nextStepperFlag & (1 << i)) ) {
      steppers[i].di -= tmpCtr;
      continue;
    }

    volatile stepperInfo& s = steppers[i];

    if ( s.stepCount < s.totalSteps ) {
      s.stepFunc();
      s.stepCount++;
      s.stepPosition += s.dir;
      if ( s.stepCount >= s.totalSteps ) {
        s.movementDone = true;
        remainingSteppersFlag &= ~(1 << i);
      }
    }

    if ( s.rampUpStepCount == 0 ) {
      s.n++;
      s.d = s.d - (2 * s.d) / (4 * s.n + 1);
      if ( s.d <= s.minStepInterval ) {
        s.d = s.minStepInterval;
        s.rampUpStepCount = s.stepCount;
      }
      if ( s.stepCount >= s.totalSteps / 2 ) {
        s.rampUpStepCount = s.stepCount;
      }
    }
    else if ( s.stepCount >= s.totalSteps - s.rampUpStepCount ) {
      s.d = (s.d * (4 * s.n + 1)) / (4 * s.n + 1 - 2);
      s.n--;
    }

    s.di = s.d; // integer
  }

  setNextInterruptInterval();

  TCNT1  = 0;
}

void runAndWait() {
  setNextInterruptInterval();
  while ( remainingSteppersFlag );
}

void loop() {

  TIMER1_INTERRUPTS_ON

  while (Serial.available() !=  0)
  {
    data = Serial.readString();

    for (a = 0; a < 3; a++)
    {
      get_data[a] = split(data, ' ', a);

      if (get_data[a] != NULL)
      {
        if (a == 0)
        {
          x = get_data[0].toInt();
        }

        if (a == 1)
        {
          y = get_data[1].toInt();
        }

        if (a == 2)
        {
          z = get_data[2].toInt();
        }
      }
    }

    if (x != NULL)
    {
      prepareMovement( 0, x );
      Serial.print("Stepper X : ");
      Serial.print(x);
      Serial.println(" steps");
    }

    if (y != 0)
    {
      prepareMovement( 1, y );
      Serial.print("Stepper Y : ");
      Serial.print(y);
      Serial.println(" steps");
    }

    if (z != 0)
    {
      prepareMovement( 2, z );
      Serial.print("Stepper Z : ");
      Serial.print(z);
      Serial.println(" steps");
      Serial.println();
    }

    runAndWait();

    x = 0;
    y = 0;
    z = 0;
  }



}

String split(String data, char character, int pos)
{
  stringData = 0;
  per_data = "";

  for (int i = 0; i < data.length() - 1; i++)
  {

    if (data[i] == character)
    {
      stringData++;
    }

    else if (stringData == pos)
    {
      per_data.concat(data[i]);
    }

    else if (stringData > pos)
    {
      return per_data;
      break;
    }
  }

  return per_data;
}

After that, open your Arduino Serial Monitor, and send the steps value in “x y z” format (make sure you are using space). For example, you want to rotate the stepper motor together with the steps position:

X is 30 steps
Y is 100 steps
Z is 360 steps


In the serial monitor form, please write 30 100 360, then Enter. You can see the motors spinning together.

To simplify this illustration, I attached a video of the program above below.

I understand that the program above still has many shortcomings, but it all went well and I have tested it with my stepper.

If this article is useful, please share it with your friends, hopefully this can help your project.

Have a nice day.