Multiple HC-SR04 sensors not playing well. Advice?

For my senior project I was instructed to design a collision avoidance system for a golf cart. After arguing with my professor about how LIDAR would be much more reliable, he is convinced that using these HC-SR04 sensors is the way to go due to budget. I'm prototyping the system using three of these sensors on a breadboard, but I think the sensors are interfering with each other. From left to right we have sensor #1, sensor #2, and sensor #3. When any of these sensors are programmed alone, they work great and exactly as I want them to. When sensor #2 is programmed in with either of the others, I get a 0in return. When sensor #1 and sensor #3 are programmed together (the two furthest apart), but only one returning information, it has a very long delay. They won't be mounted near this close on the golf cart, but will I still have issues?

I'm very new to Arduino and much of what I have done so far was taken from the internet and recoded to fit my needs. Here is my current code with most of sensor #2 programming commented out. If this code sucks, please go easy but I'm willing to listen to constructive criticism.

const int trigPin1 = 9;
const int echoPin1 = 8;
const int trigPin2 = 7;
const int echoPin2 = 6;
const int trigPin3 = 5;
const int echoPin3 = 4;

// constants won't change. Used here to set a pin number :
const int redLed = 3;      // the number of the LED pin
const int yellowLed = 2;
const int greenLed = 10;


void setup() {
  // initialize serial communication:
  Serial.begin(9600);
  pinMode(redLed, OUTPUT);
  pinMode(yellowLed, OUTPUT);
  pinMode(greenLed, OUTPUT);
  

 
}

void loop()
{
  // establish variables for duration of the ping, 
  // and the distance result in inches and centimeters:
  long duration1, inches1, cm1;
  long duration2, inches2, cm2;
  long duration3, inches3, cm3;

  // The sensor is triggered by a HIGH pulse of 10 or more microseconds.
  // Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
 pinMode(trigPin1, OUTPUT);
  digitalWrite(trigPin1, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin1, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin1, LOW);

  /*pinMode(trigPin2, OUTPUT);
  digitalWrite(trigPin2, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin2, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin2, LOW);
*/
  pinMode(trigPin3, OUTPUT);
  digitalWrite(trigPin3, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin3, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin3, LOW);


  // Read the signal from the sensor: a HIGH pulse whose
  // duration is the time (in microseconds) from the sending
  // of the ping to the reception of its echo off of an object.
  pinMode(echoPin1, INPUT);
  duration1 = pulseIn(echoPin1, HIGH);

  /*pinMode(echoPin2, INPUT);
  duration2 = pulseIn(echoPin2, HIGH);
*/
  pinMode(echoPin3, INPUT);
  duration3 = pulseIn(echoPin3, HIGH);

  // convert the time into a distance
  inches1 = microsecondsToInches(duration1);
  cm1 = microsecondsToCentimeters(duration1);

  inches2 = microsecondsToInches(duration2);
  cm2 = microsecondsToCentimeters(duration2);

 inches3 = microsecondsToInches(duration3);
  cm3 = microsecondsToCentimeters(duration3);

  digitalWrite(greenLed, HIGH);

  if (inches1 < 15){
    digitalWrite(yellowLed, HIGH);
  }

  else {
    digitalWrite(yellowLed, LOW);
  }

  if (inches1 < 7) {
    digitalWrite(redLed, HIGH);
  }

  else {
    digitalWrite(redLed, LOW);
  }


  
  Serial.print(inches1);
  Serial.print("in, ");
  Serial.print(cm1);
  Serial.print("cm");
  Serial.println();



  delay(100);
  
  

}

long microsecondsToInches(long microseconds)
{
  // According to Parallax's datasheet for the PING))), there are
  // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
  // second).  This gives the distance travelled by the ping, outbound
  // and return, so we divide by 2 to get the distance of the obstacle.
  // See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
  return microseconds / 74 / 2;

 
}

long microsecondsToCentimeters(long microseconds)
{
  // The speed of sound is 340 m/s or 29 microseconds per centimeter.
  // The ping travels out and back, so to find the distance of the
  // object we take half of the distance travelled.
  return microseconds / 29 / 2;
}

You are right and the LIDAR would be much better. The LidarLite V2 from Pulsedlight.com supposed to be available again.

The ultrasonic sensors do interfere with each other. You have to trigger each one and wait for its return independently, then move on to the next in sequence.

jremington:
You are right and the LIDAR would be much better. The LidarLite V2 from Pulsedlight.com supposed to be available again.

The ultrasonic sensors do interfere with each other. You have to trigger each one and wait for its return independently, then move on to the next in sequence.

That is the exact sensor I proposed. I even had a mirror system almost ready to 3D print and he shot the idea down due to cost. Once he sees that these sensors are junk for what I need to do, he will realize he should have let me order the LIDAR.

What if there isn't a return within a reasonable time? For instance, if there isn't an object within 15ft of the cart and there is no return for the sensor that just fired, is it possible to cancel the echo and move to the next sensor? Or can I just fire the next sensor anyways?

Edit: I just realized that "cancelling an echo" is idiotic. Firing the next sensor with the echo returning from the first would give a false reading.

I proposed that the two outside sensors should be mounted at an angle, about 18 inches from the middle sensor, pointing inwards. With this setup, if a sensor reads an echo from any of the other sensors, it shouldn't be an issue right? Wouldn't that mean that there should be an object in the way of the cart?

The HC-SR04 sensors are supposed to have a timeout if no echo returns, but many of them are reported to be buggy and get stuck. You will have to test each one individually. I'm not sure if there is a workaround, but Google may know.

With improper timing it is possible for one sensor to detect the direct transmission from another, so there is no guarantee that you are seeing an echo.

Tell your prof that you get (only sometimes) what you pay for!

triumph_013:
What if there isn't a return within a reasonable time? For instance, if there isn't an object within 15ft of the cart and there is no return for the sensor that just fired, is it possible to cancel the echo and move to the next sensor? Or can I just fire the next sensor anyways?

You can set a timeout for the 'pulseIn()' function. (Read the documentation in the 'Reference'.)

Also, you could consider using the "NewPing" library, too, rather than doing it all manually. It supports multiple sensors.

I don't see the issue with polling each sensor in sequence.

So lets take your 15ft (and convert to a sensible unit :stuck_out_tongue: 4.57 meters) and calculate the maximum time it would take to get a response from an object in that range.

Speed of sound in air is 343 m/s.

therefore max time for a sensor reading is: (4.57*2)/343 = 0.027 seconds.

A golf buggy speed is about 24km/h or 6.6667 m/s. If you have 3 sensors like you propose your total time to get 3 readings would be 0.081 seconds and the golf buggy will travel 0.54m.

This (while allot further than i had originally expected) gives you plenty of time to poll each sensor a few times before it will get anywhere close to the object you would collide with. However these sensors have a fairly narrow field of view and i'd suggest that you will need more than 3 of them to no hit poles which might sit in the blind spots hah!

All this being said On a golf buggy I very much doubt that 3 sensor pointing different directions (-45Deg, 0Def, 45Deg) would ever interfere.

p.s. I've been using these in a bounce counting system for trampolines and considered using 2 right next to each other to increase reliability (The trampoline bed is not the best material for rebounding the sound) however realised that i was going to have to poll them in sequence anyway removing the advantage of using 2 over taking 2 readings.

OldSteve:
Also, you could consider using the "NewPing" library, too, rather than doing it all manually. It supports multiple sensors.

Just tried using the NewPing library and the example given. After changing the code to work with the three sensors I have, it doesn't work. The serial monitor is spitting out some random garbage. Not sure what I did wrong.

#include <NewPing.h>


    // ---------------------------------------------------------
    // This example code was used to successfully communicate
    // with 15 ultrasonic sensors. You can adjust the number of
    // sensors in your project by changing SONAR_NUM and the
    // number of NewPing objects in the "sonar" array. You also
    // need to change the pins for each sensor for the NewPing
    // objects. Each sensor is pinged at 33ms intervals. So, one
    // cycle of all sensors takes 495ms (33 * 15 = 495ms). The
    // results are sent to the "oneSensorCycle" function which
    // currently just displays the distance data. Your project
    // would normally process the sensor results in this
    // function (for example, decide if a robot needs to turn
    // and call the turn function). Keep in mind this example is
    // event-driven. Your complete sketch needs to be written so
    // there's no "delay" commands and the loop() cycles at
    // faster than a 33ms rate. If other processes take longer
    // than 33ms, you'll need to increase PING_INTERVAL so it
    // doesn't get behind.
    // ---------------------------------------------------------
    
     
    #define SONAR_NUM     3 // Number or sensors.
    #define MAX_DISTANCE 200 // Max distance in cm.
    #define PING_INTERVAL 33 // Milliseconds between pings.
    #define redLed 3
    #define yellowLed 2
    #define greenLed 10
     
    unsigned long pingTimer[SONAR_NUM]; // When each pings.
    unsigned int cm[SONAR_NUM]; // Store ping distances.
    uint8_t currentSensor = 0; // Which sensor is active.
     
    NewPing sonar[SONAR_NUM] = { // Sensor object array.
      NewPing(9, 8, MAX_DISTANCE),
      NewPing(7, 6, MAX_DISTANCE),
      NewPing(5, 4, MAX_DISTANCE),
    };
     
    void setup() {
      Serial.begin(115200);
      pingTimer[0] = millis() + 75; // First ping start in ms.
      for (uint8_t i = 1; i < SONAR_NUM; i++)
        pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;

          pinMode(redLed, OUTPUT);
          pinMode(yellowLed, OUTPUT);
          pinMode(greenLed, OUTPUT);

        
    }
     
    void loop() {
      for (uint8_t i = 0; i < SONAR_NUM; i++) {
        if (millis() >= pingTimer[i]) {
          pingTimer[i] += PING_INTERVAL * SONAR_NUM;
          if (i == 0 && currentSensor == SONAR_NUM - 1)
            oneSensorCycle(); // Do something with results.
          sonar[currentSensor].timer_stop();
          currentSensor = i;
          cm[currentSensor] = 0;
          sonar[currentSensor].ping_timer(echoCheck);
        }
      }
      // The rest of your code would go here.

     /* digitalWrite(greenLed, HIGH);
      
      if (cm < 15) {
        digitalWrite(yellowLed, HIGH);
      }

      else {
        digitalWrite(yellowLed, LOW);
      }

      if (cm < 7) {
        digitalWrite(redLed, HIGH);
      }

      else {
        digitalWrite(redLed, LOW);
      }
     */
    }
     
    void echoCheck() { // If ping echo, set distance to array.
      if (sonar[currentSensor].check_timer())
        cm[currentSensor] = sonar[currentSensor].ping_result / US_ROUNDTRIP_CM;
    }
     
    void oneSensorCycle() { // Do something with the results.
      for (uint8_t i = 0; i < SONAR_NUM; i++) {
        Serial.print(i);
        Serial.print("=");
        Serial.print(cm[i]);
        Serial.print("cm ");
      }
      Serial.println();
    }

triumph_013:
The serial monitor is spitting out some random garbage. Not sure what I did wrong.

If the serial monitor is just printing random garbage, it's more likely to be a serial problem than a "NewPing" problem. Did you set the serial monitor baud rate to match that of your code?

OldSteve:
Did you set the serial monitor baud rate to match that of your code?

No idea what that means. I just copy and pasted the code and changed a few things dealing with the number of sensors since the original code was for 15. I also changed the pins for the sensors.

triumph_013:
No idea what that means. I just copy and pasted the code and changed a few things dealing with the number of sensors since the original code was for 15. I also changed the pins for the sensors.

The code has the serial baud rate set at 115200 baud. Your serial monitor needs to have the same baud rate set, or you'll just see random garbage. Alternatively, check the current baud rate of the serial monitor and change the setting in the code to match.

Edit: In your original posted code, the baud rate was set to 9600 baud, here:-
Serial.begin(9600);Change your "NewPing" code from 115200 to 9600 and see how you go.

OldSteve:
The code has the serial baud rate set at 115200 baud. Your serial monitor needs to have the same baud rate set, or you'll just see random garbage. Alternatively, check the current baud rate of the serial monitor and change the setting in the code to match.

Edit: In your original posted code, the baud rate was set to 9600 baud, here:-
Serial.begin(9600);Change your "NewPing" code from 115200 to 9600 and see how you go.

Yeah I finally figured that part out. Google was my friend. I tried to edit my other post to reflect this and it seems that I have a maximum post limit and it wouldn't let me edit or make another post.

Everything is working great now. My LEDs are working as they should, now I need to add a "speed sensor" to the code so that the warning distance changes with speed. I will be using a hall effect sensor mounted to the rear wheel in the final design, but for now I will be using a potentiometer to fake it haha.

I appreciate the help thus far. Thanks guys/gals