Using ultrasonic sensor to detect wall when robot is driving

I have attached an ultrasonic sensor to my robot to make it stop when close to a wall.

The user interface sends a forward command when the 'drive forward' button is pressed, and a stop command when it is released. In my code, the ultrasonic sensor only take measurements when robot receives a forward command. If the distance on the sensor is less then 10 cm, the rover will stop and back up a little bit.

However I am having two problems at the moment:

  1. If i tap the 'drive forward' button the robot drives forward for longer then the time i tapped the button, as if there is some lag in the system.

  2. When the ultrasonic sensor is making reading, almost every time the last value returned is much lower then the other values, sometimes making the readings below 10cm and thereby stopping the robot.

How do i code this more correctly then what i have done?

void loop() {
  ESPcom();

  if (echoFlag1 == true){
    echo1();
  }
  if (echoFlag2 == true){
    echo2();
  }
  ledControl();
}


void doCommands(char payloadCmd) {
  if (payloadCmd == '0') {            // 0 = Stop
    carStop();
    echoFlag1 = false;
    echoFlag2 = false;
    Serial.println("STOP");
  }

  else if (payloadCmd == '1') {       // 1 = Forward
      carAdvance(100, 100);
      echoFlag1 = true;
      echoFlag2 = false;
      Serial.println("FORWARD");
    }
  

  else if (payloadCmd == '2') {       // 2 = Left
      carTurnLeft(150, 150);
      echoFlag1 = false;
      echoFlag2 = false;
      Serial.println("LEFT");
  }

  else if (payloadCmd == '3') {      // 3 = Right
      carTurnRight(150, 150);
      echoFlag1 = false;
      echoFlag2 = false;
      Serial.println("RIGHT");

  }
  
  else if (payloadCmd == '4') {      // 4 = Backwards
      carBack(100, 100);
      echoFlag1 = false;
      echoFlag2 = true;
      Serial.println("BACK");
  }


void echo1() {
  Serial.println("ECHO");
  long duration, distance;
  digitalWrite(trigPin1, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin1, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin1, LOW);
  duration = pulseIn(echoPin1, HIGH);
  distance = (duration / 2) / 29.1;
  delay(10);

  if (distance > 0 && distance < 10) {
    carStop();
    tone(buzzerPin, 1200, 1000);
    carBack(75,75);
    delay(700);
    carStop();
    delay(200);
  }
}

Get rid of delay() and use millis() for timing. There are many resources to do timing with miillis() on this forum alone.

  1. That's not all you're all your code so we're unable to tell you whre the lag is comming from. But my guess, you use some more delay()'s...

  2. From the datasheet:

we
suggest to use over 60ms
measurement cycle, in order to prevent trigger sign
al to the echo signal.

Do you actually do that?

Small tip, numbering things is stupid. In case of variables, use an array. In case of functions, give it a damn parameter.

Extra tip, keep functions small. The name of the function should tell everything the function does. each1() does wayyyyy more then sending out an echo :wink:

septillion:

  1. That's not all you're all your code so we're unable to tell you whre the lag is comming from. But my guess, you use some more delay()'s...

  2. From the datasheet:Do you actually do that?

Small tip, numbering things is stupid. In case of variables, use an array. In case of functions, give it a damn parameter.

Extra tip, keep functions small. The name of the function should tell everything the function does. each1() does wayyyyy more then sending out an echo :wink:

The communication receiver code is this:

void ESPcom() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (ESPserial.available() > 0 && newData == false) {
    rc = ESPserial.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        payload[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        payload[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
  
  if (newData == true) {
    doCommands(payload[0]);
    newData = false;
  }
}

I have changed the echo functions to use millis instead of delay for timing:

void echo1() {
  Serial.println("ECHO");
  long duration, distance;
  digitalWrite(trigPin1, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin1, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin1, LOW);
  duration = pulseIn(echoPin1, HIGH);
  distance = (duration / 2) / 29.1;
  delay(10);
  
  if (distance > 0 && distance < 10) {
    carBack(75,75);
    currentMillis = millis();
    if (currentMillis - startMillis >= 500){
    carStop();
    startMillis = currentMillis;
    }
  }
}

I still use delayMicroseconds though, as this is the way it's done in every example i've seen with this sensor.

I still have both issues where the robot wont stop at the correct time when tapping the forward button, as well as false readings on the sensor...

I doubt changing to millis() in the echo function will fix it.

delayMicroseconds() isn't that bad, that's just a couple of ticks. delay(1) is delayMicroseconds(1000) aka 3 orders a long :wink:

But if you want real help, post all code in a compilable way. Don't let us copy and past back the code into the whole program.

You are spending time in pulseIn(), too.

Look at the NewPing library. It uses interrupts to handle the reading of the echo, without the need for any blocking code.