How can I run two loops at the same time?

Hi, I have two codes that work perfectly fine, but I would like to combine them into one. One of the codes is to send data to my phone through Bluetooth, and the other to control a little car based on the distance detected by some ultrasonic sensors. The problem is that the loop of this code contains some delays which would cause the data to stop being transferred to my phone. Any ideas? Use code tags to format code for the forum. Here are the two codes:
Code one:

 #include <Servo.h>

Servo rf;
Servo rb;
Servo lf;
Servo lb;
Servo headx;
Servo headz;

//motor drivers connections
byte m1A1 = 37;   
byte m1A2 = 39;  
byte m2A1 = 41;
byte m2A2 = 43;
byte m3A1 = 26;
byte m3A2 = 28;
byte m4A1 = 29;
byte m4A2 = 31;
byte m5A1 = 33;
byte m5A2 = 35;
byte m6A1 = 34;
byte m6A2 = 36;

//wheel angle manuver
byte servostep = 4;
byte distancechange = 20;
byte timetocheck = 3000;

//wheels initial
byte inrf = 88;
byte inlf = 92;
byte inrb = 94;
byte inlb = 92;

//wheels right
byte rightrf = 101;
byte rightlf = 105;
byte rightrb = 107;
byte rightlb = 105;

///wheels left
byte leftrf = 75;
byte leftlf = 79;
byte leftrb = 81;
byte leftlb = 79;

//front wheels set value
int leftRF = leftrf;
int leftLF = leftlf;
int rightRF = rightrf;
int rightLF = rightlf;

//back wheels set value
int leftRB = leftrb;
int leftLB = leftlb;
int rightRB = rightrb;
int rightLB = rightlb;

//servo pins
byte rfpin = 4;  
byte rbpin = 5; 
byte lfpin = 2; 
byte lbpin = 3;
byte headxpin= 6;
byte headzpin = 9;

// Define ultrasonic sensor pins
byte echoL = 10;     
byte triggerL = 11;  
byte echoM = 12;     
byte triggerM = 13;  
byte echoR = 22;     
byte triggerR = 24;  

// Define servo objects

int headxcurrent = 93;
byte headxin = 93;
byte headzin = 90;
int headzcurrent = 90;
int headxsteps = 10;
int headzsteps = 10;

// Define distance thresholds
byte minDistance = 30;  // Minimum distance in cm
byte maxDistance = 120;  // Maximum distance in cm


// Function to calculate distance 
int getDistance(int triggerPin, int echoPin) {
  // Send trigger pulse
  digitalWrite(triggerPin, LOW);
  delayMicroseconds(2);
  digitalWrite(triggerPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(triggerPin, LOW);

  // Read echo pulse duration
  long duration = pulseIn(echoPin, HIGH);

  // Calculate distance in cm
  int distance = duration * 0.034 / 2;

  return distance;
 }


void setup() {

  //head code start
  headx.attach(headxpin);
  headz.attach(headzpin);
  headx.write(93);
  headz.write(90);
  //head code end
  
  // Initialize servo objects
  rf.attach(rfpin);
  rb.attach(rbpin);
  lf.attach(lfpin);
  lb.attach(lbpin);

  // Initialize ultrasonic sensor pins
  pinMode(triggerL, OUTPUT);
  pinMode(echoL, INPUT);
  pinMode(triggerM, OUTPUT);
  pinMode(echoM, INPUT);
  pinMode(triggerR, OUTPUT);
  pinMode(echoR, INPUT);

  // Set initial servo positions
  lf.write(inlf);
  lb.write(inlb);
  rf.write(inrf);
  rb.write(inrb);

  // Wait for servo initialization
  delay(500);

  // Set motor control pins as outputs
  pinMode(m1A1, OUTPUT);
  pinMode(m1A2, OUTPUT);
  pinMode(m2A1, OUTPUT);
  pinMode(m2A2, OUTPUT);
  pinMode(m3A1, OUTPUT);
  pinMode(m3A2, OUTPUT);
  pinMode(m4A1, OUTPUT);
  pinMode(m4A2, OUTPUT);
  pinMode(m5A1, OUTPUT);
  pinMode(m5A2, OUTPUT);
  pinMode(m6A1, OUTPUT);
  pinMode(m6A2, OUTPUT);


}

void loop() {

  // Read distances from ultrasonic sensors
  int distanceL = getDistance(triggerL, echoL);
  int distanceM = getDistance(triggerM, echoM);
  int distanceR = getDistance(triggerR, echoR);
    digitalWrite(m1A1, HIGH);
    digitalWrite(m1A2, LOW);
    digitalWrite(m2A1, LOW);
    digitalWrite(m2A2, HIGH);
    digitalWrite(m3A1, LOW);
    digitalWrite(m3A2, HIGH);
    digitalWrite(m4A1, HIGH);
    digitalWrite(m4A2, LOW);
    digitalWrite(m5A1, HIGH);
    digitalWrite(m5A2, LOW);
    digitalWrite(m6A1, HIGH);
    digitalWrite(m6A2, LOW);
    
  //move right; hit left
  if (distanceL >= minDistance && distanceL <= maxDistance) {
    rf.write(inrf);
    lf.write(inlf);
    rb.write(inrb);
    lb.write(inlb);
    digitalWrite(m1A1, LOW);
    digitalWrite(m1A2, LOW);
    digitalWrite(m2A1, LOW);
    digitalWrite(m2A2, LOW);
    digitalWrite(m3A1, LOW);
    digitalWrite(m3A2, LOW);
    digitalWrite(m4A1, LOW);
    digitalWrite(m4A2, LOW);
    digitalWrite(m5A1, LOW);
    digitalWrite(m5A2, LOW);
    digitalWrite(m6A1, LOW);
    digitalWrite(m6A2, LOW);
    delay(500);
    rf.write(rightrf);
    lf.write(rightlf);
    delay(700);
    digitalWrite(m1A1, HIGH);
    digitalWrite(m1A2, LOW);
    digitalWrite(m2A1, LOW);
    digitalWrite(m2A2, HIGH);
    digitalWrite(m3A1, LOW);
    digitalWrite(m3A2, HIGH);
    digitalWrite(m4A1, HIGH);
    digitalWrite(m4A2, LOW);
    digitalWrite(m5A1, HIGH);
    digitalWrite(m5A2, LOW);
    digitalWrite(m6A1, HIGH);
    digitalWrite(m6A2, LOW);
    delay(3000);
    rf.write(inrf);
    lf.write(inlf);
  }

  // Move right or left; hit middle
  if (distanceM >= minDistance && distanceM <= maxDistance) {
    rf.write(inrf);
    lf.write(inlf);
    rb.write(inrb);
    lb.write(inlb);
    digitalWrite(m1A1, LOW);
    digitalWrite(m1A2, LOW);
    digitalWrite(m2A1, LOW);
    digitalWrite(m2A2, LOW);
    digitalWrite(m3A1, LOW);
    digitalWrite(m3A2, LOW);
    digitalWrite(m4A1, LOW);
    digitalWrite(m4A2, LOW);
    digitalWrite(m5A1, LOW);
    digitalWrite(m5A2, LOW);
    digitalWrite(m6A1, LOW);
    digitalWrite(m6A2, LOW);
    delay(500);
      if(distanceR > distanceL){
        rb.write(leftrb);
        lb.write(leftlb);
      }
        else if(distanceR < distanceL){
          rb.write(leftrb);
          lb.write(leftlb);
        }
    delay(700);
    //backwards
    digitalWrite(m1A1, LOW);
    digitalWrite(m1A2, HIGH);
    digitalWrite(m2A1, HIGH);
    digitalWrite(m2A2, LOW);
    digitalWrite(m3A1, HIGH);
    digitalWrite(m3A2, LOW);
    digitalWrite(m4A1, LOW);
    digitalWrite(m4A2, HIGH);
    digitalWrite(m5A1, LOW);
    digitalWrite(m5A2, HIGH);
    digitalWrite(m6A1, LOW);
    digitalWrite(m6A2, HIGH);
    delay(2200); 
    //stop
    rb.write(inrb);
    lb.write(inlb);
    delay(700);
    digitalWrite(m1A1, LOW);
    digitalWrite(m1A2, LOW);
    digitalWrite(m2A1, LOW);
    digitalWrite(m2A2, LOW);
    digitalWrite(m3A1, LOW);
    digitalWrite(m3A2, LOW);
    digitalWrite(m4A1, LOW);
    digitalWrite(m4A2, LOW);
    digitalWrite(m5A1, LOW);
    digitalWrite(m5A2, LOW);
    digitalWrite(m6A1, LOW);
    digitalWrite(m6A2, LOW);
    delay(500);
    //check for all distances again
      if(distanceR > distanceL){
        rb.write(leftrf);
        lb.write(leftlf);
      }
        else if(distanceR < distanceL){
          rb.write(rightrf);
          lb.write(rightlf);
        }
          else if (distanceM > distanceL && distanceR){
            rf.write(inrf);
            lf.write(inlf);
            rb.write(inrb);
            lb.write(inlb);
          }
    
    delay(700);
    digitalWrite(m1A1, HIGH);
    digitalWrite(m1A2, LOW);
    digitalWrite(m2A1, LOW);
    digitalWrite(m2A2, HIGH);
    digitalWrite(m3A1, LOW);
    digitalWrite(m3A2, HIGH);
    digitalWrite(m4A1, HIGH);
    digitalWrite(m4A2, LOW);
    digitalWrite(m5A1, HIGH);
    digitalWrite(m5A2, LOW);
    digitalWrite(m6A1, HIGH);
    digitalWrite(m6A2, LOW);
    delay(3000);
    rf.write(inrf);
    lf.write(inlf);
    rb.write(inrb);
    lb.write(inlb);
    
    
  }

  // move left; hit rigth
  if (distanceR >= minDistance && distanceR <= maxDistance) {
    rf.write(inrf);
    lf.write(inlf);
    rb.write(inrb);
    lb.write(inlb);
    digitalWrite(m1A1, LOW);
    digitalWrite(m1A2, LOW);
    digitalWrite(m2A1, LOW);
    digitalWrite(m2A2, LOW);
    digitalWrite(m3A1, LOW);
    digitalWrite(m3A2, LOW);
    digitalWrite(m4A1, LOW);
    digitalWrite(m4A2, LOW);
    digitalWrite(m5A1, LOW);
    digitalWrite(m5A2, LOW);
    digitalWrite(m6A1, LOW);
    digitalWrite(m6A2, LOW);
    delay(700);
    rf.write(leftrf);
    lf.write(leftlf); 
    delay(2000);
    digitalWrite(m1A1, HIGH);
    digitalWrite(m1A2, LOW);
    digitalWrite(m2A1, LOW);
    digitalWrite(m2A2, HIGH);
    digitalWrite(m3A1, LOW);
    digitalWrite(m3A2, HIGH);
    digitalWrite(m4A1, HIGH);
    digitalWrite(m4A2, LOW);
    digitalWrite(m5A1, HIGH);
    digitalWrite(m5A2, LOW);
    digitalWrite(m6A1, HIGH);
    digitalWrite(m6A2, LOW);
    delay(3000);
       rf.write(inrf);
       lf.write(inlf);
      }
  
      
  // Delay before next iteration
  delay(100);
  }

Code two:

 #include <DHT.h>

#define DHT_PIN_BELLOW 23
#define DHT_PIN_INSIDE 25
#define DHT_PIN_ABOVE 27
#define DHT_TYPE DHT11

DHT dht_bellow(DHT_PIN_BELLOW, DHT_TYPE);
DHT dht_inside(DHT_PIN_INSIDE, DHT_TYPE);
DHT dht_above(DHT_PIN_ABOVE, DHT_TYPE);

const int trigPinLeft = 11;
const int echoPinLeft = 10;
const int trigPinMiddle = 13;
const int echoPinMiddle = 12;
const int trigPinRight = 22;
const int echoPinRight = 24;

// Define the analog input pins for the gas sensors
const int gas1Pin = A0;
const int gas2Pin = A1;

int measureDistance(int trigPin, int echoPin);

void setup() {
  // Initialize serial communication with the computer
  Serial.begin(9600);

  // Initialize Bluetooth serial communication on Serial3 (RX3: pin 15, TX3: pin 14)
  Serial3.begin(9600);

  dht_bellow.begin();
  dht_inside.begin();
  dht_above.begin();

  // Set the ultrasonic sensor pins as inputs and outputs
  pinMode(trigPinLeft, OUTPUT);
  pinMode(echoPinLeft, INPUT);
  pinMode(trigPinMiddle, OUTPUT);
  pinMode(echoPinMiddle, INPUT);
  pinMode(trigPinRight, OUTPUT);
  pinMode(echoPinRight, INPUT);
}

void loop() {
  // Measure distances from ultrasonic sensors
  int distanceLeft = measureDistance(trigPinLeft, echoPinLeft);
  int distanceMiddle = measureDistance(trigPinMiddle, echoPinMiddle);
  int distanceRight = measureDistance(trigPinRight, echoPinRight);

  // Read gas sensor values
  int gas1Value = analogRead(gas1Pin);
  int gas2Value = analogRead(gas2Pin);

  // Temperature sensor values
  float temperature_bellow = dht_bellow.readTemperature();
  float humidity_bellow = dht_bellow.readHumidity();
  float temperature_inside = dht_inside.readTemperature();
  float humidity_inside = dht_inside.readHumidity();
  float temperature_above = dht_above.readTemperature();
  float humidity_above = dht_above.readHumidity();

  // Send sensor data in CSV format through Bluetooth (Serial3)
  Serial3.print(distanceLeft);
  Serial3.print(",");
  Serial3.print(distanceMiddle);
  Serial3.print(",");
  Serial3.print(distanceRight);
  Serial3.print(",");
  Serial3.print(gas1Value);
  Serial3.print(",");
  Serial3.print(gas2Value);
  Serial3.print(",");
  Serial3.print(temperature_bellow);
  Serial3.print(",");
  Serial3.print(humidity_bellow);
  Serial3.print(",");
  Serial3.print(temperature_inside);
  Serial3.print(",");
  Serial3.print(humidity_inside);
  Serial3.print(",");
  Serial3.print(temperature_above);
  Serial3.print(",");
  Serial3.println(humidity_above);

  // Send sensor data to the computer through the USB serial as well (optional)
  Serial.print(distanceLeft);
  Serial.print(",");
  Serial.print(distanceMiddle);
  Serial.print(",");
  Serial.print(distanceRight);
  Serial.print(",");
  Serial.print(gas1Value);
  Serial.print(",");
  Serial.print(gas2Value);
  Serial.print(",");
  Serial.print(temperature_bellow);
  Serial.print(",");
  Serial.print(humidity_bellow);
  Serial.print(",");
  Serial.print(temperature_inside);
  Serial.print(",");
  Serial.print(humidity_inside);
  Serial.print(",");
  Serial.print(temperature_above);
  Serial.print(",");
  Serial.println(humidity_above);

  delay(1000); // Delay for 1 second
}

// Function to measure distance using ultrasonic sensor
int measureDistance(int trigPin, int echoPin) {
  // Send ultrasonic pulse
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  // Measure the duration of the echo
  long duration = pulseIn(echoPin, HIGH);

  // Calculate the distance
  int distance = duration * 0.034 / 2;

  return distance;
}

Get rid of the delays.

1 Like

Replace the delay by a counter, so you don't block the execution. As long as you have only one CPU core, you won't be able to run them in parallel. But sequentially and achieve the delays by counting up a counter.

1 Like

Study the "How to do several things at the same time" tutorial on this forum, which shows you have to avoid delay().

1 Like

The second code is easy to get rid of the delays so it can share processing time, but the first code requires complete re-thinking in order to share processing since it depends heavily on delay for control.

Sure it is possible to do both things (almost) in parallel.
But it is really true what @DaveX says

did you read? re-thinking

The idea is to do a very often repeating comparising of how much time has passed by since a reference-point in time.
And this is something completely different than "freeze microcontroller for 1000 milliseconds"

It really requires re-thinking. It is called non-blocking timing.
I have written a tutorial about it and I claim that it is easier to understand than most other tutorials about non-blocking timing

and as the second thing to learn is how to apply a state-machine to your code

each time your "delay"-code has a delay() in function loop
this delay() must be replaced by a state of the state-machine
The transition to the next state happens if the number of milliseconds have passed by
This is done by continiuosly comparing a timestamp against how much time has passed by since the timestamp has been stored

More details see the tutorial

best regards Stefan

2 Likes

I really need then in order to execute the movements

I'm utilizing a ELEGOO MEGA 2560 R3, which is a single-core board. Do you think the counter might be the simplest option?

Got it, I will read it right now. Thanks!

Yeah, the first code really needs to follow a timeline in order to achieve its purpose. I don't really think there's a way to avoid using these delays but I'm looking forward to read all the comments to see if any of them work.

Thanks! I'll read your tutorial. I think next time I will get a Raspberry Pi board instead; they are multicore meaning I can perform both codes in parallel, right?

One awkward trick to avoid completely the re-thinking and re-writing the first code's loop() would be to replace the blocking delay()s in the first code's loop() with a doOtherThingsWhileDelaying() function that gives a opportunity for the other non-blocking loop()'s code to run.

Something like this scheme:

void setup() {
  Serial.begin(115200);
}

void loop() {
  // Serial code with delay()s replaced by special cooperative delay function:
  Serial.print("\nA");
  doOtherThingsWhileDelaying(1000);
  Serial.print('B');
  doOtherThingsWhileDelaying(1500);
  Serial.print('C');
  doOtherThingsWhileDelaying(500);
  Serial.print('D');
  doOtherThingsWhileDelaying(1000);
}

void doOtherThingsWhileDelaying(unsigned long msDelay){
  unsigned long startTime = millis();
  while(millis() - startTime < msDelay){
  otherThing2();
  otherThing3();
  otherThing4();
  //...
  }
} 

bool otherThing2(void){
  const unsigned long interval = 333;
  static unsigned long last = millis();
  if(millis() - last < interval) return false;
  last = millis();
  Serial.print('2');
  return true;
}

bool otherThing3(void){
  const unsigned long interval = 777;
  static unsigned long last = millis();
  if(millis() - last < interval) return false;
  last = millis();
  Serial.print('3');
  return true;
}

bool otherThing4(void){
   if(Serial.available()){
    char ch = Serial.read();
    Serial.print(ch);
    return true;
   }
  return false;
}

prints:

A2232B232223C22D32232
A2232B232232C2hello
23D2232
A2322B232232C232D2232
A2322B322322C232D2322

Folks don't normally recommend that method because you still have to re-write all the other N-1 tasks to be be non-blocking in order to cooperate with the new function, the new function itself, and still edit the blocking code. It's a bit sloppy and poorly designed--As long as you are re-writing the N-1 codes to be non-blocking, you might as well write all of them to be non-blocking and call them all from loop():

void loop(void){
  nonBlockingThing1();
  nonBlockingThing2();
  nonBlockingThing3();
}

ETA: here's some toy code that demonstrates all three of these methods:

// for https://forum.arduino.cc/t/how-can-i-run-two-loops-at-the-same-time/1151388/12?u=davex
// https://wokwi.com/projects/371242906116762625

void setup() {
  Serial.begin(115200);
}

void loop(void) {
  // choose one scheme:
  switch (3) {
    case 1: loopTimeLineDelays();  break;// use delays() to run a timeline
    case 2: loopTimeLine(); break; // do stuff during the delays in the timeline
    case 3: loopNonBlocking(); break; // do a non-blocking timeline with a state machine
  }
}

void loopTimeLineDelays() {
  // Serial code
  Serial.print("\nA");
  delay(1000);
  Serial.print('B');
  delay(1500);
  Serial.print('C');
  delay(500);
  Serial.print('D');
  delay(1000);
  // try the other actions after the serial timeline runs:
  otherThing2();
  otherThing3();
  otherThing4();
}

void loopTimeLine() {
  // Serial code with delay()s replaced by special cooperative delay function:
  Serial.print("\nA");
  doOtherThingsWhileDelaying(1000);
  Serial.print('B');
  doOtherThingsWhileDelaying(1500);
  Serial.print('C');
  doOtherThingsWhileDelaying(500);
  Serial.print('D');
  doOtherThingsWhileDelaying(1000);
}

void loopNonBlocking() {
  nonBlockingThing1();
  otherThing2();
  otherThing3();
  otherThing4();
}

void doOtherThingsWhileDelaying(unsigned long msDelay) {
  unsigned long startTime = millis();
  while (millis() - startTime < msDelay) {
    otherThing2();
    otherThing3();
    otherThing4();
    //...
  }
}

bool otherThing2(void) {
  const unsigned long interval = 333;
  static unsigned long last = millis();
  if (millis() - last < interval) return false;
  last = millis();
  Serial.print('2');
  return true;
}

bool otherThing3(void) {
  const unsigned long interval = 777;
  static unsigned long last = millis();
  if (millis() - last < interval) return false;
  last = millis();
  Serial.print('3');
  return true;
}

bool otherThing4(void) {
  if (Serial.available()) {
    char ch = Serial.read();
    Serial.print(ch);
    return true;
  }
  return false;
}

void  nonBlockingThing1(void) {
  static int state = 1;
  static unsigned long interval = 0;
  static unsigned long last = 0;
  if (millis() - last >= interval) {
    last = millis();
    switch (state) {
      case 1:
        Serial.print("\nA");
        interval = 1000;
        state = 2;
        break;
      case 2:
        Serial.print('B');
        interval = 1500;
        state = 3;
        break;
      case 3:
        Serial.print('C');
        interval = 500;
        state = 4;
        break;
      case 4:
        Serial.print('D');
        state = 1;
        interval = 1000;
        break;
    }
  }
}

There really is a way to avoid using those delays. Stefan (and several other sources) have good tutorials on event-loop -based programming, which is what one needs to lean to make (at least the other N-1) multiple loops work together.

The trick is turning a timeline into a series of contingent events with a state machine, which is an important, basic skill in event-loop programming.

Yes, delays must go..
But first, combine redundant code into function calls..
Give this a test..

#include <Servo.h>

Servo rf;
Servo rb;
Servo lf;
Servo lb;
Servo headx;
Servo headz;

//motor drivers connections
byte m1A1 = 37;
byte m1A2 = 39;
byte m2A1 = 41;
byte m2A2 = 43;
byte m3A1 = 26;
byte m3A2 = 28;
byte m4A1 = 29;
byte m4A2 = 31;
byte m5A1 = 33;
byte m5A2 = 35;
byte m6A1 = 34;
byte m6A2 = 36;

//wheel angle manuver
byte servostep = 4;
byte distancechange = 20;
byte timetocheck = 3000;

//wheels initial
byte inrf = 88;
byte inlf = 92;
byte inrb = 94;
byte inlb = 92;

//wheels right
byte rightrf = 101;
byte rightlf = 105;
byte rightrb = 107;
byte rightlb = 105;

///wheels left
byte leftrf = 75;
byte leftlf = 79;
byte leftrb = 81;
byte leftlb = 79;

//front wheels set value
int leftRF = leftrf;
int leftLF = leftlf;
int rightRF = rightrf;
int rightLF = rightlf;

//back wheels set value
int leftRB = leftrb;
int leftLB = leftlb;
int rightRB = rightrb;
int rightLB = rightlb;

//servo pins
byte rfpin = 4;
byte rbpin = 5;
byte lfpin = 2;
byte lbpin = 3;
byte headxpin = 6;
byte headzpin = 9;

// Define ultrasonic sensor pins
byte echoL = 10;
byte triggerL = 11;
byte echoM = 12;
byte triggerM = 13;
byte echoR = 22;
byte triggerR = 24;

// Define servo objects

int headxcurrent = 93;
byte headxin = 93;
byte headzin = 90;
int headzcurrent = 90;
int headxsteps = 10;
int headzsteps = 10;

// Define distance thresholds
byte minDistance = 30;  // Minimum distance in cm
byte maxDistance = 120;  // Maximum distance in cm


// Function to calculate distance
int getDistance(int triggerPin, int echoPin) {
  // Send trigger pulse
  digitalWrite(triggerPin, LOW);
  delayMicroseconds(2);
  digitalWrite(triggerPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(triggerPin, LOW);

  // Read echo pulse duration
  long duration = pulseIn(echoPin, HIGH);

  // Calculate distance in cm
  int distance = duration * 0.034 / 2;

  return distance;
}


void setup() {

  //head code start
  headx.attach(headxpin);
  headz.attach(headzpin);
  headx.write(93);
  headz.write(90);
  //head code end

  // Initialize servo objects
  rf.attach(rfpin);
  rb.attach(rbpin);
  lf.attach(lfpin);
  lb.attach(lbpin);

  // Initialize ultrasonic sensor pins
  pinMode(triggerL, OUTPUT);
  pinMode(echoL, INPUT);
  pinMode(triggerM, OUTPUT);
  pinMode(echoM, INPUT);
  pinMode(triggerR, OUTPUT);
  pinMode(echoR, INPUT);

  // Set initial servo positions
  lf.write(inlf);
  lb.write(inlb);
  rf.write(inrf);
  rb.write(inrb);

  // Wait for servo initialization
  delay(500);

  // Set motor control pins as outputs
  pinMode(m1A1, OUTPUT);
  pinMode(m1A2, OUTPUT);
  pinMode(m2A1, OUTPUT);
  pinMode(m2A2, OUTPUT);
  pinMode(m3A1, OUTPUT);
  pinMode(m3A2, OUTPUT);
  pinMode(m4A1, OUTPUT);
  pinMode(m4A2, OUTPUT);
  pinMode(m5A1, OUTPUT);
  pinMode(m5A2, OUTPUT);
  pinMode(m6A1, OUTPUT);
  pinMode(m6A2, OUTPUT);


}

void loop() {

  // Read distances from ultrasonic sensors
  int distanceL = getDistance(triggerL, echoL);
  int distanceM = getDistance(triggerM, echoM);
  int distanceR = getDistance(triggerR, echoR);

  Forward();

  //move right; hit left
  if (distanceL >= minDistance && distanceL <= maxDistance) {
    rf.write(inrf);
    lf.write(inlf);
    rb.write(inrb);
    lb.write(inlb);
    AllStop();
    delay(500);
    rf.write(rightrf);
    lf.write(rightlf);
    delay(700);
    Forward();
    delay(3000);
    rf.write(inrf);
    lf.write(inlf);
  }

  // Move right or left; hit middle
  if (distanceM >= minDistance && distanceM <= maxDistance) {
    rf.write(inrf);
    lf.write(inlf);
    rb.write(inrb);
    lb.write(inlb);
    AllStop();
    delay(500);
    if (distanceR > distanceL) {
      rb.write(leftrb);
      lb.write(leftlb);
    }
    else if (distanceR < distanceL) {
      rb.write(leftrb);
      lb.write(leftlb);
    }
    delay(700);
    //backwards
    Backwards();
    delay(2200);
    //stop
    rb.write(inrb);
    lb.write(inlb);
    delay(700);
    AllStop();
    delay(500);
    //check for all distances again
    if (distanceR > distanceL) {
      rb.write(leftrf);
      lb.write(leftlf);
    }
    else if (distanceR < distanceL) {
      rb.write(rightrf);
      lb.write(rightlf);
    }
    else if (distanceM > distanceL && distanceR) {
      rf.write(inrf);
      lf.write(inlf);
      rb.write(inrb);
      lb.write(inlb);
    }

    delay(700);
    Forward();
    delay(3000);
    rf.write(inrf);
    lf.write(inlf);
    rb.write(inrb);
    lb.write(inlb);
  }

  // move left; hit rigth
  if (distanceR >= minDistance && distanceR <= maxDistance) {
    rf.write(inrf);
    lf.write(inlf);
    rb.write(inrb);
    lb.write(inlb);
    AllStop();
    delay(700);
    rf.write(leftrf);
    lf.write(leftlf);
    delay(2000);
    Forward();
    delay(3000);
    rf.write(inrf);
    lf.write(inlf);
  }


  // Delay before next iteration
  delay(100);
}

void AllStop() {
  digitalWrite(m1A1, LOW);
  digitalWrite(m1A2, LOW);
  digitalWrite(m2A1, LOW);
  digitalWrite(m2A2, LOW);
  digitalWrite(m3A1, LOW);
  digitalWrite(m3A2, LOW);
  digitalWrite(m4A1, LOW);
  digitalWrite(m4A2, LOW);
  digitalWrite(m5A1, LOW);
  digitalWrite(m5A2, LOW);
  digitalWrite(m6A1, LOW);
  digitalWrite(m6A2, LOW);
}

void Forward() {
  digitalWrite(m1A1, HIGH);
  digitalWrite(m1A2, LOW);
  digitalWrite(m2A1, LOW);
  digitalWrite(m2A2, HIGH);
  digitalWrite(m3A1, LOW);
  digitalWrite(m3A2, HIGH);
  digitalWrite(m4A1, HIGH);
  digitalWrite(m4A2, LOW);
  digitalWrite(m5A1, HIGH);
  digitalWrite(m5A2, LOW);
  digitalWrite(m6A1, HIGH);
  digitalWrite(m6A2, LOW);
}

void Backwards() {
  digitalWrite(m1A1, LOW);
  digitalWrite(m1A2, HIGH);
  digitalWrite(m2A1, HIGH);
  digitalWrite(m2A2, LOW);
  digitalWrite(m3A1, HIGH);
  digitalWrite(m3A2, LOW);
  digitalWrite(m4A1, LOW);
  digitalWrite(m4A2, HIGH);
  digitalWrite(m5A1, LOW);
  digitalWrite(m5A2, HIGH);
  digitalWrite(m6A1, LOW);
  digitalWrite(m6A2, HIGH);
}

should be the same..
Simmed here..

~q

The delays can be replaced by time-code with no regards to the rest of the code which could use a bit of reorganizing into shorter steps to work smoother.

I have a 2 sketch demo about combining sketches. The first one keeps the delays and the second one replaces them with software timers. It demonstrates different problems including loops with delays in them.
The code is something to keep and at first run to see the output differences.

Any entry from Serial Monitor (115200 baud!) will pause the run, the next restarts it. Here's the delay-using combined codes.

// CombineSketchesBlocked 2022 by GoForSmoke @ Arduino.cc Forum
// Free to post anywhere complete and as is.

// This is the delay-codes combined to show how bad it is.

// LoopCounter value
#define microsInOneSecond 1000000UL

// PrintNumbers variables
unsigned long pnWait = 1000UL; 
int setNum = 60;
int printNum = 0;

// BlinkPattern variables
const byte ledPin = 13;
byte indexMax = 12;
byte index;

enum blinkStates {BLINK_1_ON, BLINK_1_OFF, BLINK_2_ON, BLINK_2_OFF, BLINK_3_ON, BLINK_3_OFF};
blinkStates blinkStep; // state tracking for BlinkPattern() below


void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Combine Sketches Blocked, free by GoForSmoke" ));
  Serial.println( F( "This sketch shows how NOT to combine sketches.\n" ));
  Serial.println( F( "Press Enter to toggle monitor scrolling." ));

  pinMode( ledPin, OUTPUT );
  
  digitalWrite( ledPin, HIGH );  // 2 secs before the scroll starts
  delay( 500 );
  digitalWrite( ledPin, LOW );
  delay( 500 );
  digitalWrite( ledPin, HIGH );
  delay( 500 );
  digitalWrite( ledPin, LOW );
  delay( 500 );

  blinkStep = BLINK_1_ON;  // actual value is 0
};


// LoopCounter -- void loop() code to count loops per second
void LoopCounter() // tells the average response speed of void loop()
{ 
  delay( 1000 );
  Serial.println( "1" );
}


// PrintNumbers -- void loop() code to count from one value to another with wait between.
void PrintNumbers()  // a loop with a delay in it becomes...
{
  for ( printNum = 0; printNum <= setNum; printNum++ )
  {
    Serial.print( F( "Number " ));
    Serial.print( printNum );
    Serial.print( F( "   Time " ));
    Serial.println( millis() );
    delay( pnWait );
  }

  printNum = 0;
}


// BlinkPattern -- void loop() code to blink led13 using delay()
void BlinkPattern()  // does the same as above without delay()
{
  digitalWrite( ledPin, HIGH );   // --  BLINK_1_ON
  Serial.print( F( "BLINK_1_ON, time " ));
  Serial.println( millis());
  delay( 500 );
  digitalWrite( ledPin, LOW );    // --  BLINK_1_OFF
  Serial.print( F( "BLINK_1_OFF, time " ));
  Serial.println( millis());
  delay( 500 );
  for ( byte i = 0; i < 12; i++ )
  {
    digitalWrite( ledPin, HIGH );   // --  BLINK_2_ON
    Serial.print( F( "BLINK_2_ON, time " ));
    Serial.println( millis());
    delay( 250 );
    digitalWrite( ledPin, LOW );    // --  BLINK_2_OFF
    Serial.print( F( "BLINK_2_OFF, time " ));
    Serial.println( millis());
    delay( 250 );
  }
  digitalWrite( ledPin, HIGH );   // --  BLINK_3_ON
  Serial.print( F( "BLINK_3_ON, time " ));
  Serial.println( millis());
  delay( 1000 );
  digitalWrite( ledPin, LOW );    // --  BLINK_3_OFF
  Serial.print( F( "BLINK_3_ON, time " ));
  Serial.println( millis());
  delay( 1000 );
}


void StopGo()  // user keyboard block / unblock 
{
  if ( Serial.available() )
  {
    while ( Serial.available() )  // clearing the buffer
    {
      Serial.read();
      delay(1); // don't care
    }
  }
  else
  {
    return;
  }
  
  while ( ! Serial.available() ); // waiting for Go, ! is logical NOT
  
  while ( Serial.available() )  // clearing the buffer
  {
    Serial.read();
    delay(1); // don't care
  }
}

void loop()  // runs over and over, see how often
{            
  LoopCounter(); // the function runs as a task, the optimizer will inline the code.
  PrintNumbers();
  BlinkPattern();
  StopGo();
}

And the non-blocking combinationl


// CombineSketchesDemo 2022 by GoForSmoke @ Arduino.cc Forum
// Free for use, May 23, 2022 by GFS. Compiled on Arduino 2.1.0.5
// Free to post anywhere complete and as is.

// This sketch shows a general method to get rid of delays in code.
// You could upgrade code that delays to combine into this sketch.

// .. adding delays in loop cases
// revisions with forum help:
// dlloyd --  added state enums May 11. <--- changed as needed.

////////////////////////////////////////////////////////////////////////////

// This example/demo takes 3 sketches that use delay() and COMBINE THEM
// by using simple techniques the sketch demonstrates.
// Plus there's a user stop/go that violates how the 3 together work.

// Code that does not sit or loop in one spot to wait, 
// does not block other code from executing during a wait.
// In 1 ms, code can use or lose 16000 cpu cycles. So don't block!

// The 3 delay-sketches void loop() code goes into 3 functions that run as tasks. 
// Arduino void loop() runs the same functions over and over, these functions don't wait
// but instead check time and if time's not up then run the next function.
// if ( Serial.available() ) is just such a thing, not waiting around for data.

// Every delay() has a built-in timer. I replace it with a timer that only runs when set.
// The timer is first in the function so that the function can return until timeout.
// The timer only runs when the time to wait is set > 0, else the function continues.

// Where a delay was removed, the time is set and the timer runs for the next many loops.
// When the time is done, the time to wait set = 0. What was a delay() sets the timer. 

// This demo also addresses loops inside of void loop(), which can hog cycles terribly.
// It also contains a state machine, a code tool of value beyond what the demo does.
// A State Machine is code written in steps of what to do according to what's been done.
// A state variable holds what to do next time the machine runs.
// perhaps the 1st state waits for input and when it gets it the code changes state..
// to run what to do with the input. State Machines can run inside of state machines.

// task StopGo. --  Block - unBlock on a keystroke to stop serial monitor 

// task LoopCounter  --  lets you know how often void loop() ran last second.
// LoopCounter value
#define microsInOneSecond 1000000UL

// task PrintNumbers --  prints from printNum to setNum, pnWait ms apart
// PrintNumbers variables
unsigned long pnStart;
unsigned long pnWait = 1000UL; 
int setNum = 60;
int printNum = 0;

// task BlinkPattern --  blinks led13
// BlinkPattern variables
const byte ledPin = 13;
unsigned long blinkStart;
unsigned long blinkWait; 
byte indexMax = 12;
byte index;

enum blinkStates {BLINK_1_ON, BLINK_1_OFF, BLINK_2_ON, BLINK_2_OFF, BLINK_3_ON, BLINK_3_OFF};
blinkStates blinkStep; // state tracking for BlinkPattern() below


void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Combine Sketches Demo, free by GoForSmoke" ));
  Serial.println( F( "This sketch shows how to combine sketches.\n" ));
  Serial.println( F( "Press Enter to toggle monitor scrolling." ));

  pinMode( ledPin, OUTPUT );
  
  digitalWrite( ledPin, HIGH );  // 2 secs before the scroll starts
  delay( 500 );
  digitalWrite( ledPin, LOW );
  delay( 500 );
  digitalWrite( ledPin, HIGH );
  delay( 500 );
  digitalWrite( ledPin, LOW );
  delay( 500 );

  blinkStep = BLINK_1_ON;  // actual value is 0
};


/* LoopCounter -- void loop() code to count loops per second
*
*  delay( 1000 );
*  Serial.println( "1" );
*/

void LoopCounter() // tells the average response speed of void loop()
{ // inside a function, static variables keep their value from run to run
  static unsigned long count, countStartMicros; // only this function sees these

  count++; // adds 1 to count after any use in an expression, here it just adds 1.
  if ( micros() - countStartMicros >= microsInOneSecond ) // 1 second
  {
    countStartMicros += microsInOneSecond; // for a regular second
    Serial.println( count ); // 32-bit binary into decimal text = many micros
    count = 0; // don't forget to reset the counter 
  }
}


/* PrintNumbers -- void loop() code to count from one value to another with wait between.
*
* for ( printNum = 0; printNum <= setNum; printNum++ )
* {
*   Serial.print( F( "Number " ));
*   Serial.print( printNum );
*   Serial.print( F( "   Time " ));
*   Serial.println( millis() );
*   delay( pnWait );
* }
*/

// this task runs once. suppose that StopGo made it start again?

void PrintNumbers()  // a loop with a delay in it becomes...
{
  if ( setNum < 1 )  return; // how to turn this task off, setNum = 0;
  
  // This repeat timer replaces delay()  
  // start of repeat timer
  if ( millis() - pnStart < pnWait )  // wait is not over
  {
    return; // instead of blocking, the undelayed function returns
  }

  Serial.print( F( "Number " ));
  Serial.print( printNum );
  Serial.print( F( "   Time " ));
  Serial.println( millis() );
  
  pnStart += pnWait; // starting 1 sec after last start, not the same as = millis()

  if ( ++printNum >= setNum )  
  { 
    setNum = printNum = 0;
  }
}


/* BlinkPattern -- void loop() code to blink led13 using delay()
 * 
 * digitalWrite( ledPin, HIGH );   --  BLINK_1_ON
 * Serial.print( F( "BLINK_1_ON, time " ));
 * Serial.println( millis());
 * delay( 500 );
 * digitalWrite( ledPin, LOW );    --  BLINK_1_OFF
 * Serial.print( F( "BLINK_1_OFF, time " ));
 * Serial.println( millis());
 * delay( 500 );
 * for ( i = 0; i < 12; i++ )
 * (
 *   digitalWrite( ledPin, HIGH );   --  BLINK_2_ON
 *   Serial.print( F( "BLINK_2_ON, time " ));
 *   Serial.println( millis());
 *   delay( 250 );
 *   digitalWrite( ledPin, LOW );    --  BLINK_2_OFF
 *   Serial.print( F( "BLINK_2_OFF, time " ));
 *   Serial.println( millis());
 *   delay( 250 );
 * }
 * digitalWrite( ledPin, HIGH );   --  BLINK_3_ON
 * Serial.print( F( "BLINK_3_ON, time " ));
 * Serial.println( millis());
 * delay( 1000 );
 * digitalWrite( ledPin, LOW );    --  BLINK_3_OFF
 * Serial.print( F( "BLINK_3_ON, time " ));
 * Serial.println( millis());
 * delay( 1000 );
 */

void BlinkPattern()  // does the same as above without delay()
{
  // This one-shot timer replaces every delay() removed in one spot.  
  // start of one-shot timer
  if ( blinkWait > 0 ) // one-shot timer only runs when set
  {
    if ( millis() - blinkStart < blinkWait )
    {
      return; // instead of blocking, the undelayed function returns
    }
    else
    {
      blinkWait = 0; // time's up! turn off the timer and run the blinkStep case
    }
  }
  // end of one-shot timer

  // here each case has a timed wait but cases could change Step on pin or serial events.
  switch( blinkStep )  // runs the case numbered in blinkStep
  {
    case BLINK_1_ON :
    digitalWrite( ledPin, HIGH );
    Serial.print( F( "BLINK_1_ON, time " ));
    Serial.println( blinkStart = millis()); // able to set a var to a value I pass to function
    blinkWait = 500; // for the next half second, this function will return on entry.
    blinkStep = BLINK_1_OFF;   // when the switch-case runs again it will be case 1 that runs
    break; // exit switch-case

    case BLINK_1_OFF :
    digitalWrite( ledPin, LOW );
    Serial.print( F( "BLINK_1_OFF, time " ));
    Serial.println( blinkStart = millis());
    blinkWait = 500;
    blinkStep = BLINK_2_ON;
    break;

    case BLINK_2_ON :
    digitalWrite( ledPin, HIGH );
    Serial.print( F( "BLINK_2_ON, time " ));
    Serial.println( blinkStart = millis());
    blinkWait = 250;
    blinkStep = BLINK_2_OFF;
    break;

    case BLINK_2_OFF :
    digitalWrite( ledPin, LOW );
    Serial.print( F( "BLINK_2_OFF, time " ));
    Serial.println( blinkStart = millis());
    blinkWait = 250;
    // ******  this replaces the for-loop in non-blocking code.  ******
    if ( index++ < indexMax ) // index gets incremented after the compare
    {
      blinkStep = BLINK_2_ON;
    }
    else
    {
      index = 0;
      blinkStep = BLINK_3_ON;
    }  // end of how to for-loop in a state machine without blocking execution.
    break;

    case BLINK_3_ON :
    digitalWrite( ledPin, HIGH );
    Serial.print( F( "BLINK_3_ON, time " ));
    Serial.println( blinkStart = millis());
    blinkWait = 1000;
    blinkStep = BLINK_3_OFF;
    break;

    case BLINK_3_OFF :
    digitalWrite( ledPin, LOW );
    Serial.print( F( "BLINK_3_OFF, time " ));
    Serial.println( blinkStart = millis());
    blinkWait = 1000;
    blinkStep = BLINK_1_ON; // start again
    break;
  }
}


void StopGo()  // user keyboard block / unblock 
{
  if ( Serial.available() )
  {
    while ( Serial.available() )  // clearing the buffer
    {
      Serial.read();
      delay(1); // don't care
    }
  }
  else
  {
    return;
  }
  
  while ( ! Serial.available() ); // waiting for Go, ! is logical NOT
  
  while ( Serial.available() )  // clearing the buffer
  {
    Serial.read();
    delay(1); // don't care
  }
}

void loop()  // runs over and over, see how often
{            
  LoopCounter(); // the function runs as a task, the optimizer will inline the code.
  PrintNumbers();
  BlinkPattern();
  StopGo();
}
1 Like

The counter is more complicated solution that delay. But the only way of doing it in non-blocking way. Probably even a combination of counters/state machines in your case.

It is down to technique and the comparison is laughable.

Using timers lets many sketches to be combined and run together.
Running multiple sketches is possible but the complexity to do that rises geometrically with every added bit.

I was converting some GMS code, just the startup lines had over 12 delays so I'm looking at making a state machine twice that many states and all having a timer when I see that a single one-shot timer before the state machine will do the job for all of the states without a replicated timer for each state as the first thing it does, setting an interval uses that one-shot timer before the switch-case. The first thing is the timer, the case does the rest when the timer finishes and turns itself off.

void function() { 1-shot-timer; state-machine; }

1 Like

Something like this?

You can use Dual-core 30-pin ESP32S Board for concurrent execution of two loops using functions of FreeRTOS.

As we are on multicore. The propeller-chip from parallax can run up to 7 tasks really independant from each other. Each task in his own core. And in a much simpler way of coding than using FreeRTOS. The syntax of the programming language SPIN is similar to C++.