Trying to run two motors simultaneously using millis()

Hi, I am trying to run 2 motors simultaneously and need to stop one after 5s and other after 7s. I used millis() function, but only first motor is starting and it is not stopping after 5s, but when manually stopped with limit switch, it stopped and the second motor starts to rotate. can anyone please help me with this.

void onOpnBtnChange() 
{
  if(opn_btn == 1)
  {
    counter = 1;
  }
  if(counter == 1)
  {
    unsigned long start_time = millis();
    digitalWrite(lock1_relay, LOW);
    delay(750);
    forward_2(255);
    forward(255);
    if((millis() - start_time) >=5000)
     {
       stop_2();
     }
    if((millis() - start_time)>=7000)
     {
       stop_1();
       counter = 0;
     } 
    
  } 
}

Hello ca_paulose and welcome to the forum.
The combination of the millis() and delay() function isn“t a good solution.
To avoid using the delay() function, which blocks the expected real-time behaviour, design your own timer manager.
It is recommended to start with the mother of all Arduino timers, the IDE's BLINKWITHOUTDELAY example.
Using this example, you can see how millis() function works and extend this with some additional functions.

  1. startTimer() --> starts the timer
  2. stopTimer() --> stops the timer
    and
  3. actionTimer() --> performs an action when the timer is triggered.

Have a nice day and enjoy programming in C++ and learning.
Errors and omissions excepted.

Welcome to the forum

Please post your complete sketch so that the problem can be seen in context

I am using IOT cloud with esp32. The bottom most function is used for used for the current situation.

 CloudLight light;
  CloudSwitch cls_btn;
  CloudSwitch opn_btn;
  bool button;
  bool button2;

  Variables which are marked as READ/WRITE in the Cloud Thing will also have functions
  which are called when their values are changed from the Dashboard.
  These functions are generated with the Thing and added at the end of this sketch.
*/
#define L_EN 12
#define R_EN 13
#define L_PWM 14
#define R_PWM 27
#define lock1_relay 26
#define light_relay 18
#define open_lim1 22
#define close_lim1 33
#define L_EN2 5
#define R_EN2 17
#define L_PWM2 16
#define R_PWM2 4
int counter;
#include "thingProperties.h"

void setup()
 {
 
  Serial.begin(9600);
  pinMode(lock1_relay, OUTPUT);
  pinMode(light_relay, OUTPUT);
  pinMode(L_EN, OUTPUT);
  pinMode(R_EN, OUTPUT);
  digitalWrite(L_EN, LOW);
  digitalWrite(R_EN, LOW);

  ledcAttachPin(L_PWM, 0);
  ledcAttachPin(R_PWM, 1);

  ledcSetup(0, 12000, 8); // 12 kHz PWM, 8-bit resolution
  ledcSetup(1, 12000, 8);

  ledcWrite(0, 0);
  ledcWrite(1, 0);
  
  pinMode(L_EN2, OUTPUT);
  pinMode(R_EN2, OUTPUT);
  digitalWrite(L_EN2, LOW);
  digitalWrite(R_EN2, LOW);

  ledcAttachPin(L_PWM2, 2);
  ledcAttachPin(R_PWM2, 3);

  ledcSetup(0, 12000, 8); // 12 kHz PWM, 8-bit resolution
  ledcSetup(1, 12000, 8);

  ledcWrite(2, 0);
  ledcWrite(3, 0);
  
  pinMode(open_lim1, INPUT_PULLUP);
  pinMode(close_lim1, INPUT_PULLUP);
    
  delay(1500); 

  // Defined in thingProperties.h
  initProperties();

  // Connect to Arduino IoT Cloud
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();
}

void forward(int value)
{
  digitalWrite(L_EN, HIGH);
  digitalWrite(R_EN, HIGH);
  ledcWrite(0, value);
  ledcWrite(1, 0);
}

void backward(int value)
{
  digitalWrite(L_EN, HIGH);
  digitalWrite(R_EN, HIGH);
  ledcWrite(0, 0);
  ledcWrite(1, value);
}

void stop_1()
{
  digitalWrite(L_EN, LOW);
  digitalWrite(R_EN, LOW);
  ledcWrite(0, 0);
  ledcWrite(1, 0);
}
void forward_2(int value)
{
  digitalWrite(L_EN2, HIGH);
  digitalWrite(R_EN2, HIGH);
  ledcWrite(2, value);
  ledcWrite(3, 0);
}

void backward_2(int value)
{
  digitalWrite(L_EN2, HIGH);
  digitalWrite(R_EN2, HIGH);
  ledcWrite(2, 0);
  ledcWrite(3, value);
}

void stop_2()
{
  digitalWrite(L_EN2, LOW);
  digitalWrite(R_EN2, LOW);
  ledcWrite(2, 0);
  ledcWrite(3, 0);
}

void loop() 
{
  ArduinoCloud.update();

  int op_lim1 = digitalRead(open_lim1);
  int cl_lim1 = digitalRead(close_lim1);
  if(op_lim1 == LOW)
    {
      stop_1();
      delay(750);
      digitalWrite(lock1_relay, HIGH);
    }
    
  if(cl_lim1 == LOW)
    {
      stop_1();
      delay(750);
      digitalWrite(lock1_relay, HIGH);
    }  
  
  
}

/*
  Since Button is READ_WRITE variable, onButtonChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onButtonChange() 
{
  
  if(button == 1)
  {
    digitalWrite(lock1_relay, LOW);
    delay(750);
    forward(255);
    delay(11750);
    for(int i =255; i>=115; i-=10)
     {
       forward(i);
       delay(100);
     }
   
  }
  
}
/*
  Since Button2 is READ_WRITE variable, onButton2Change() is
  executed every time a new value is received from IoT Cloud.
*/
void onButton2Change() 
{
  if(button2 == 1)
  {
    digitalWrite(lock1_relay, LOW);
    delay(750);
    backward(255);
    delay(11500);
    for(int i =255; i>=115; i-=10)
     {
       backward(i);
       delay(100);
     }
  }  
 
}

void onLightChange()
{
  if( light == 1)
  {
     digitalWrite( light_relay, LOW);
  }
  else if(light == 0)
  {
    digitalWrite( light_relay, HIGH);
  }
}

void onClsBtnChange() 
{
  
}


void onOpnBtnChange() 
{
  if(opn_btn == 1)
  {
    counter = 1;
  }
  if(counter == 1)
  {
    unsigned long start_time = millis();
    digitalWrite(lock1_relay, LOW);
    delay(750);
    forward_2(255);
    forward(255);
    if((millis() - start_time) >=5000)
     {
       stop_2();
     }
    if((millis() - start_time)>=7000)
     {
       stop_1();
       counter = 0;
     } 
    
  } 
}

consider following which demonstrates handling multiple buttons, Leds and timers using arrays

// demonstrate use of multiple timers

const byte LedPin [] = { 11, 12, 13 };
const byte ButPin [] = { A1, A2, A3 };
#define N_Pin    sizeof(ButPin)

unsigned long msecLst [N_Pin];
unsigned long Period [N_Pin]  = { 2000, 4000 };

byte butState [N_Pin];

enum { Off = HIGH, On = LOW };

// -----------------------------------------------------------------------------
void
loop (void)
{
    unsigned long msec = millis ();

    // check if time expired for each pin
    for (unsigned i = 0; i < N_Pin; i++)  {
        if (msecLst [i] && (msec - msecLst [i]) >= Period [i])  {
            msecLst [i] = 0;
            digitalWrite (LedPin [i], Off);
        }
    }

    // check if button pressed
    for (unsigned i = 0; i < N_Pin; i++)  {
        byte but = digitalRead (ButPin [i]);

        if (butState [i] != but)  {
            butState [i] = but;

            // capture timestamp for corresponding pin
            if (LOW == but)  {
                msecLst [i] = 0 == msec ? 1 : msec;
                digitalWrite (LedPin [i], On);
            }
        }
    }
}

// -----------------------------------------------------------------------------
void
setup (void)
{
    Serial.begin (9600);

    // enable pullup resistor for each button pin and init state
    for (unsigned i = 0; i < N_Pin; i++)  {
        pinMode (ButPin [i], INPUT_PULLUP);
        butState [i] = digitalRead (ButPin [i]);
    }

    // enable each output and set to Off
    for (unsigned i = 0; i < N_Pin; i++)  {
        digitalWrite (LedPin [i], Off);
        pinMode      (LedPin [i], OUTPUT);
    }
}

to stay close to your code
You should give your variables names that are spot-on self-explaining

define two global variables

boolean runMotors;
unsigned long start_time;

and modify your onOpnBtnChange-function to this

void onOpnBtnChange() {
  if(opn_btn == 1 && !runMotors) { // if motors have NOT yet started
    runMotors = true; // enable time-checking (done by if( runMotors )
    start_time = millis(); // store snapshot of time ONLY when motors START
  }
  
  if( runMotors ) {
    digitalWrite(lock1_relay, LOW);
    delay(750); // this delay blocks EVERYTHING 
    // your code will react ONLY AFTER the delay has finished
    forward_2(255);
    forward(255);
    
    if((millis() - start_time) >= 5000){
       stop_2();
     }
     
    if((millis() - start_time) >= 7000) {
       stop_1();
       runMotors = false; // disable time-checking
     }     
  }

best regards Stefan

How often is onOpnBtnChange() called? It looks like it would be called only when the "OpnBtn" changes. If that is true, then when "OpnBtn" is not changing, the timers are not being checked.

The timers should be checked frequently. Usually in loop() or in a function called every time through loop(). Try separating the timer check from onOpnBtnChange() and calling checkOpnTimers() from loop():

// Make this global so onOpnBtnChange() and 
// checkOpnTimers() can share it.
unsigned long OpnStartTime;

void onOpnBtnChange()
{
  if (opn_btn == 1)
  {
    counter = 1;
    OpnStartTime = millis();
    digitalWrite(lock1_relay, LOW);
    delay(750);
    forward_2(255);
    forward(255);
  }
}

void checkOpnTimers()
{
  if (counter == 1)
  {
    if ((millis() - OpnStartTime) >= 5000)
    {
      stop_2();
    }
    
    if ((millis() - OpnStartTime) >= 7000)
    {
      stop_1();
      counter = 0;
    }
  }
}

I tried this, but no changes.

void onOpnBtnChange() is called when the button is pressed from arduino iot mobile app.
The situation is that I am using 2 motors for gate opening and closing. 1st gate is 90 degree swing and 2nd is 180 degree swing, so both need to be stopped at different times.

That's what I figured. That's why your timers don't work as expected. That's why I recommended those changes to your sketch.

How /when to call the checkOpnTimer() function.

As I said:

The timers should be checked frequently. Usually in loop() or in a function called every time through loop().

Try separating the timer check from onOpnBtnChange() and calling checkOpnTimers() from loop():

I'm willing to help. But only under one condition:

You post a pretty detailed description of the functionality.

The following bolded capital letters are not meant as "shouting"
It is just a very high emphasising

functionality means

NO CODE ! JUST NORMAL WORDS

I made the experience that some users always try to talk about details of coding.
As long as other users do not have the "big picture" it is hard to give good advice.

This is the reason to post a normal worded description of the whole project and a normal worded description of the wanted functionality

There are two diametral different approaches to code:
.

  1. linear sequence

  2. circular repeating through void loop()

circular repeating means void loop() is looping fast.
As fast as possible. Things that should only happen from time to time or at a low frequency
are executed only from time to time with non-blocking timing

You have to understand circular-repeating in combination with non-blocking timing first
especially the difference between linear sequence using delay()
and
circular-repeating which

totally avoids delay()

through non-blocking timing

edit: in post #7 I expanded my non-blocking timing tutorial with a new kind of visualisation how non-blocking timing works

repeat doing things at different frequencies

best regards Stefan

The project is to make a gate opener with 2 motor, controlled by ESP32 and Arduino iot app.
I am using 2 BTS7960 motor driver for each motor and 12v 16A power supply. When I tested with one motor, it is working fine, but i need my second gate to be stopped at different time, and this is the trouble, if I use delay(), it will affect 1st motor which needs to run longer distance.
When I reduce the PWM value from 255 to 150, both motor start at same time, but one motor is not stopping as per the code.

Not sure it's reliable to stop the doors based on timing- much wiser imo to use limit switches.

yes... I am using limit switch to stop the motors, in the actual case, timer is used to slow down the motors just before stopping to avoid harsh hitting on limit switch.

Just my impression - you charged off and started programming as if you were using code as a sketching language for your design logic. That is a futile approach for most people. You need to first produce a complete design, then code. For you, what would be most important is the timing of the gate sequences as well as the lock logic. So you should produce some timing diagrams. When you draw out the complete time line, it is easy to see the dependencies of events and conditions that affect those events. Sometimes multiple time lines should be drawn to illustrate different scenarios.

Then fully familiarize yourself with the use of the millis() timing paradigm, through study or experimentation with simpler examples, and implement some code based on the timing diagrams. Do not use the delay() function at all. It will usually destroy the proper operation of millis() based timers.

If you encounter a lot of different scenarios and dependencies, you should implement a state machine in addition to using millis().

If it isn't obvious, this will render almost all your present code useless. You will need to start almost from scratch. But it can't be made to work, as it stands, so it's not such a great loss.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.