Removing Delays from Motor control Code

Hello,

I am using an Arduino Mega to control a DC motor with encoder through a Polulu H bridge (https://www.pololu.com/product/708).

The code below allows me to successfully make the motor turn backwards and forwards at predefined speeds and for my chosen number of rotations but It does not function without the delays between each command.

Is there a way to make this code work without any delays? so for example my motor will make 2 turns forwards, stop according to the ISR and then immediately proceed to making 2 turns backwards?

I am in the process of trying to use millis() looking at the blink without delay example but im struggling to transfer it to this application

// MD03A_Motor_basic + encoder

#define InA1            11                      // INA motor pin
#define InB1            12                      // INB motor pin 
#define PWM1            13                      // PWM motor pin
#define encodPinA1      21                      // encoder A pin
#define encodPinB1      20                      // encoder B pin

#define LOOPTIME        100                     // PID loop time
#define FORWARD         1                       // direction of rotation
#define BACKWARD        2                       // direction of rotation

unsigned long lastMilli = 0;                    // loop timing
unsigned long lastMilliPrint = 0;               // loop timing
volatile long count = 0;                        // rotation counter
long countInit;
long tickNumber = 0;
boolean run = false;                            // motor moves

void setup() {
 Serial.begin(9600); // baud rate
 Serial.flush();
  pinMode(InA1, OUTPUT);
  pinMode(InB1, OUTPUT);
  pinMode(PWM1, OUTPUT);
  pinMode(encodPinA1, INPUT);
  pinMode(encodPinB1, INPUT);
  digitalWrite(encodPinA1, HIGH);               // turn on pullup resistor
  digitalWrite(encodPinB1, HIGH);
  attachInterrupt(2, rencoder, FALLING);        //interrupt 1 is pin 20, 2 is pin 19
}


void loop() {
  
  moveMotor(FORWARD, 255, 20);            // direction, PWM, ticks number
  delay(7000);
  
 Serial.print("count : " );
 Serial.println(countInit); 
 Serial.println(tickNumber);
 countInit = 0;
      count = 0;
      
  moveMotor(BACKWARD, 150, 15);           // 510=360°
  delay(7000);
  
 Serial.print("count : " );
 Serial.println(countInit); 
 Serial.println(tickNumber);
 countInit = 0;
      count = 0;

  moveMotor(FORWARD, 120, 20);
  delay(7000);
  
 Serial.print("count : " );
 Serial.println(countInit); 
 Serial.println(tickNumber);

 countInit = 0;
      count = 0;

 motorOff();
  while(1) {}
  


}

void moveMotor(int direction, int PWM_val, long tick)  {
  countInit = count;    // abs(count)
  tickNumber = tick;
  if (direction == FORWARD)          motorForward(PWM_val);
  else if (direction == BACKWARD)    motorBackward(PWM_val);
}


// ********* Encoder counting ********

void rencoder()  {                                  // pulse and direction
  if (PD2)   {
    count++;    //if (digitalRead(encodPinB1)==HIGH)   count ++;
  }
 else                   {
    count--;   // if (digitalRead(encodPinB1)==LOW)   count --;
  }
  
// if (run)
 if ((count)> tickNumber)   {  
      motorBrake();       //apply the brake when the count of the motor exceeds the tick number which is what i ask for in the earlier code.
      }
    
}


// ****Code for forward backward and brake  ****

void motorForward(int PWM_val)  {
  analogWrite(PWM1, PWM_val);
  digitalWrite(InA1, LOW);
  digitalWrite(InB1, HIGH);
  run = true;
}

void motorBackward(int PWM_val)  {
  analogWrite(PWM1, PWM_val);
  digitalWrite(InA1, HIGH);
  digitalWrite(InB1, LOW);
  run = true;
}

void motorBrake()  {
  analogWrite(PWM1, 0);
  digitalWrite(InA1, HIGH);
  digitalWrite(InB1, HIGH);
  run = false;
}

void motorOff()  {
  analogWrite(PWM1, 0);
  digitalWrite(InA1, LOW);
  digitalWrite(InB1, LOW);
  run = false;
}

Here is my attempted application of millis() rather than delay, It is not working...

// MD03A_Motor_basic + encoder

#define InA1            11                      // INA motor pin
#define InB1            12                      // INB motor pin 
#define PWM1            13                      // PWM motor pin
#define encodPinA1      21                      // encoder A pin
#define encodPinB1      20                      // encoder B pin

//#define LOOPTIME        100                   // PID loop time       this would be used instead of my unsigned long interval = 1000 code
#define FORWARD         1                       // direction of rotation
#define BACKWARD        2                       // direction of rotation

unsigned long lastMilli = 0;                    // loop timing
unsigned long lastMilliPrint = 0;               // loop timing
unsigned long interval1 = 1000;                 // NB wait time between states, referenced later in code
unsigned long interval2 = 1000;
unsigned long interval3 = 1000;
unsigned long interval4 = 1000;
unsigned long previousMillis1 = 0;              //stores the last time update 
unsigned long previousMillis2 = 0;
unsigned long previousMillis3 = 0;
unsigned long previousMillis4 = 0;
volatile long count = 0;                        // rotation counter
long countInit;
long tickNumber = 0;
boolean run = false;                            // motor moves

void setup() {
Serial.begin(9600); // baud rate
Serial.flush();
 pinMode(InA1, OUTPUT);
 pinMode(InB1, OUTPUT);
 pinMode(PWM1, OUTPUT);
 pinMode(encodPinA1, INPUT);
 pinMode(encodPinB1, INPUT);
 digitalWrite(encodPinA1, HIGH);               // turn on pullup resistor
 digitalWrite(encodPinB1, HIGH);
 attachInterrupt(2, rencoder, FALLING);        //interrupt 1 is pin 20, 2 is pin 19
}


void loop() {

unsigned long currentMillis = millis();               
if(currentMillis - previousMillis1 > interval1) {       
previousMillis1 = currentMillis;

}
 moveMotor(FORWARD, 255, 20);            // direction, PWM, ticks number
// delay(7000);

Serial.print("count : " );
Serial.println(countInit); 
Serial.println(tickNumber);
countInit = 0;
     count = 0;

if(currentMillis - previousMillis2 > interval2) {       // we dont strictly need this becasue we only run once, avoids millis roll over
previousMillis2 = currentMillis;

}

 moveMotor(BACKWARD, 150, 15);           // 510=360°
// delay(7000);

Serial.print("count : " );
Serial.println(countInit); 
Serial.println(tickNumber);
countInit = 0;
     count = 0;

if(currentMillis - previousMillis3 > interval3) {       // we dont strictly need this becasue we only run once, avoids millis roll over
previousMillis3 = currentMillis;

}

 moveMotor(FORWARD, 120, 20);
// delay(7000);

Serial.print("count : " );
Serial.println(countInit); 
Serial.println(tickNumber);

countInit = 0;
     count = 0;

motorOff();
 while(1) {}



}

void moveMotor(int direction, int PWM_val, long tick)  {
 countInit = count;    // abs(count)
 tickNumber = tick;
 if (direction == FORWARD)          motorForward(PWM_val);
 else if (direction == BACKWARD)    motorBackward(PWM_val);
}


// ********* Encoder counting ********

void rencoder()  {                                  // pulse and direction
 if (PD2)   {
   count++;    //if (digitalRead(encodPinB1)==HIGH)   count ++;
 }
else                   {
   count--;   // if (digitalRead(encodPinB1)==LOW)   count --;
 }

// if (run)
if ((count)> tickNumber)   {  
     motorBrake();       //apply the brake when the count of the motor exceeds the tick number which is what i ask for in the earlier code.
     }
   
}


// ****Code for forward backward and brake  ****

void motorForward(int PWM_val)  {
 analogWrite(PWM1, PWM_val);
 digitalWrite(InA1, LOW);
 digitalWrite(InB1, HIGH);
 run = true;
}

void motorBackward(int PWM_val)  {
 analogWrite(PWM1, PWM_val);
 digitalWrite(InA1, HIGH);
 digitalWrite(InB1, LOW);
 run = true;
}

void motorBrake()  {
 analogWrite(PWM1, 0);
 digitalWrite(InA1, HIGH);
 digitalWrite(InB1, HIGH);
 run = false;
}

void motorOff()  {
 analogWrite(PWM1, 0);
 digitalWrite(InA1, LOW);
 digitalWrite(InB1, LOW);
 run = false;

This piece of code does nothing useful

if(currentMillis - previousMillis1 > interval1) { 
    previousMillis1 = currentMillis;
}

because you have nothing within the braces that does something when the time expires.

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

I don't understand why you have the comment "// we dont strictly need this becasue we only run once, avoids millis roll over" because I don't know what you think the alternative might be. It seems sensible to me always to use millis() in a way that cannot cause a rollover problem even if you don't expect your program to run for more than 47 days non-stop.

...R

Hi Robin,

I have read that link but i'm having some trouble grasping exactly what implementing millis will do, and the steps i'd have to take to do it.

My motor currently stops when the encoder count reaches the "tick" count that I specify in void loop() then pauses for the duration of my delay() until it continues onto the next movement operation. All I am trying to do is go through my movement operations without a delay.

If I implement millis() I don't want my motor movement to become time based, it must remain "tick" based. Is that possible?

I hope you can understand what I mean, I can't quiet talk the talk... never mind walk the walk.

nick1992: My motor currently stops when the encoder count reaches the "tick" count that I specify in void loop() then pauses for the duration of my delay() until it continues onto the next movement operation. All I am trying to do is go through my movement operations without a delay.

I guess then, when the tick count has been completed and the motor stops you should save millis() at that point and have code something like this pseudo code

if (okToRun == true) {
  move a step
  stepCount ++
  if stepCount >= maxSteps {
     okToRun = false
     motorStoppedMillis = millis()
     stepCount = 0
  }
}

if (millis() - motorStoppedMillis >= stopIntervalMillis) {
   okToRun =  true
}

...R

Thanks again Robin,

That code seems to do a lot of what happens in my ISR like counting and stopping the motor when step count is reached. Are you suggesting that I should add that code to my ISR, or would that pseudocode replace the commands to move the motor in void loop()? in which case would i remove...

 if ((count)> tickNumber)   {  
      motorBrake();      
      }

from my ISR?

Out of interest, what is your opinion on the difficulty and feasibility of using millis() in my code? Is it a big step or a relatively simple integration, considering I have struggled to get this far!

As a general rule put as little code as possible in an ISR.

You could have a flag variable set by the ISR when the brake condition arises and you code in loop() can test for that and call the brake() function when the flag is true.

The ISR might be like this

void rencoder()  {                                  // pulse and direction
 if (PD2)   {
   count++; 
 }
else   {
   count--; 
 }

if ((count)> tickNumber)   { 
     applyBrake = true;  
     }

}

...R

Hi Robin (if you're still there... anyone is welcome to help)

I've been reading a lot (your comments have been cropping up all over the place) and still just don't fully understand what i'm doing with millis().

I can grasp blink without delay and various other examples but I can't picture how my code would be if I was to use millis rather than delay, and i think the reason I can't understand is that all of these examples are still time based like having a bulb on for 1 interval or 500ms etc. but my project is not time based, it's angular movement or "ticks" based.

I now have working delay() based code to move 4 motors (1 at a time) a predefined number of steps, but I'd like;

  1. To never have to wait for a pause between movements
  2. To be able to move 2 or more motors a programmed number of "ticks" at the same time
  3. New requirement here... to be able to have an electromagnet on/off while motor movements are taking place.

Is what i want easily achievable and what am i failing to grasp?

nick1992: but my project is not time based, it's angular movement or "ticks" based.

That tempts me to ask why need either millis() or delay()

But to be more constructive ...

Can you write down in English (not code) the steps that your program needs to follow - with one step on each line.

For example (based on my understanding - which may be all wrong) - move forward N steps - wait for 5 seconds - move backwards M steps - etc ...R

I thought I needed millis() or delay() for it to run at all, my motors do seemingly random things if i remove the delays completely.

Based on my code now, the program follows these steps:

  • move motor 1 forward 2020 steps
  • wait for movement to be complete
  • move motor 2 forward 2560 steps
  • wait for movement to be complete
  • move motor 3 backwards 3840 steps
  • wait for movement to be complete

This continues until the motors have moved where they need to go then they all return to their home position (i’m not sure i have said, this is a robot arm with 4 motors)

What I ideally want to happen is this:

  • move motors 1,2,3 & 4 w,x,y & z steps simultaneously
  • turn on electromagnets
  • keep electromagnets on and move motors 1,2,3 & 4 w,x,y & z steps simultaneously
  • turn off electromagnets
  • move motors 1,2,3 & 4 w,x,y & z steps simultaneously to return to home

Something which i am finding odd is this; Lets take the first set of steps… i have wrote “wait for movement to be complete” rather than “delay 4 seconds” because my robot arm does not do its manoeuvre and then pause for the duration of the delay, it seems to do its manoeuvre during the delay. So if the movement of the motor takes 2 seconds and the delay is 3 seconds, it will do the movement and then wait 1 second before proceeding to the next movement. IF the delay i specify is shorter than the time it takes to complete the prior motor movement, the motor no longer stops when it reaches its encoder count, it messes up the whole program and the motors seem to do what they want.

I am assuming it is more useful to focus on the second part “What I ideally want to happen is this:” and ignore the earlier part.

If you want the 4 motors to move at the same time then you need to move each one a step at a time. You have not said if you want the 4 motors to move (for example) 2020, 2560, 3840 and 1120 steps in the exact same time or whether it is OK for some to finish before the others.

If it is OK for some to finish sooner then you just need a counter for each motor that increments with each step and stops incrementing when the total is reached. Something like

void moveMotors()
   if (motor1 is not there yet)
      move a step
      motor1count ++
   if (motor2 is not there yet)
      move a step
      motor2count ++
  // etc
}

If they all need to finish at the same time (like on my small lathe) things are a bit more complex. You need to figure out how many microsecs (or millisecs if it is slow) the longest move takes and then you need to divide the number of steps for each motor into that time to figure out the number of µsecs between steps and then get each motor to move a step at the appropriate time. The second exaample in my Simple Stepper Code illustrates the idea for 1 motor.

Either of these processes will allow you to set a flag variable (say moveCompleted = true;) when the move finishes and that variable can be used to indicate when the next action should happen - for example switching on the magnets.

…R

Thankfully they do not all need to finish at the same time at the moment, maybe in the future but i'll cross that bridge when I come to it.

I feel like I understand your proposed method which is good so I will have a go at implementing it this afternoon.

I appreciate your help and will update on my progress when I've had a go.