Multiple HC-SR04 with as few pins as possible

Hi,

I want to connect multiple ultra sonic sensors using as few pins as possible on the arduino. Since they can't be triggered at the same time I thought about connecting one pin each to the trigger pins and then join the echo pins (see attached image). Then I can trigger one and wait for the reply, then the next one and so on. That obviously doesn't work since the echo pins are low (or maybe floating) when not measuring. Basically I want an OR gate for the echo pins. Is there any way to solve this with a pull-up resistor or similar?

I don't want to use a single pin that I switch between input/output since that's quite cumbersome is my case.

sparknburn:
Basically I want an OR gate for the echo pins.

Well, you could buy one. Or try a diode OR gate if you have a couple of diodes and a resistor.

sparknburn:
Hi,

I want to connect multiple ultra sonic sensors using as few pins as possible on the arduino. Since they can't CAN be triggered at the same time ....

I have succeeded in triggering all at the same time using interrupts :slight_smile:

#define TriggerPin  2                  // output pin all trigger pins are linked to.
#define SamplesToAverage 1             // number of ping samples to average together for use
#define DelayBetweenPings 15            // delay after ping data is completely recieved all have reported in and the next trigger pulse in miliseconds (5 seems to be great)

unsigned long Timer, zTimer, xTimer; // Delay timers
volatile unsigned long SampleAge[20];
volatile int PinArray[20] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};            // List of pins that could be used as ping inputs:
unsigned long PingTime[20] ; 
volatile int ToCompleteCtr;
volatile  unsigned long PingTimeX[20]; 
volatile  int PingSamplesX[20]; 
volatile  unsigned long edgeTime[20]; 
volatile  uint8_t PCintLast[3];
volatile  uint8_t mask[3];
int PinMask[3]; 
float Measurements[20] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 
byte *MeasurementsB;

bool SerialEvent = false;                   // Detects serial imput  1
int I2CsendDataGo = 0;
uint8_t dataB[32] ; 
int c, Pin;
int x = 0;
float inches, cm;
int DegSpread = 0;                   
static int SensorCount = 0;        
volatile byte PinList[25];            

void setup() {

  Serial.begin(115200);
  Serial.println("Send 1 for Readable Data, Send 0 For Interger Data");
  SensorCount = 0;
  pinMode(TriggerPin, OUTPUT);
  // enable interrupt for pin...
  // Select the pins you want to use for input
  // pciSetup(0); // Serial Communication
  // pciSetup(1); // Serial Communication
  // pciSetup(2); //This is my trigger pin
  pciSetup(3);
  pciSetup(4);
  pciSetup(5);
  pciSetup(6);
  pciSetup(7);
  pciSetup(8);
  pciSetup(9);
 // pciSetup(10);
 // pciSetup(11); // SPI communications
 // pciSetup(12); // SPI communications
 // pciSetup(13); // SPI communications
 // pciSetup(14); // A0
 // pciSetup(15); // A1
 // pciSetup(16); // A2
 // pciSetup(17); // A3
 // pciSetup(18); // A4  i2c Communication
 // pciSetup(19); // A5  i2c Communication
 // pciSetup(20); // A6  for some clones
 // pciSetup(21); // A7  for some clones

  
}

void loop() {
  Timer = millis(); // timing events!
  PingIt(); // Manage ping data
  if (SerialEvent) SendSerialData();
}

void RadarOut( int Degrees) {
  static int x = 0;
  static bool xz = true;
  static byte Sensor = 0;
  int deg = Degrees / SensorCount;
  Sensor = 0;
  while (Sensor < SensorCount) {
    if (Degrees > 0) {
      Serial.print((Sensor * deg) );
      if (Degrees != SensorCount) {
        Serial.print("*");
      }
      Serial.print("=");

    } else {
      Serial.print("Pin#");
      Serial.print(PinList[Sensor]);
      Serial.print("=");

    }

    Serial.print(Measurements[Sensor]);
    Serial.print("cm ");
    Sensor++;
  }
}

void PingTrigger(int Pin) {
  digitalWrite(Pin, LOW);
  delayMicroseconds(1);
  digitalWrite(Pin, HIGH); // Trigger another pulse
  delayMicroseconds(5);
  digitalWrite(Pin, LOW);
}

bool AllClear() {
  return (!(PinMask[0] & PIND) && !(PinMask[1] & PINB) && !(PinMask[2] & PINC) && !ToCompleteCtr); //  all the input pins are LOW
}

void PingIt() {
  static int SampleCt = 0;
  static bool DelayLatch = true;
  if ( AllClear()) { // Wait
    if (DelayLatch) {
      xTimer = Timer + DelayBetweenPings;
      DelayLatch = false;
    }
    if ((Timer - xTimer) >= 0 ) {
      for (int i = 0; i < 20; i++) {
      if (SampleCt >= SamplesToAverage) {
        byte Sensor = 0;
        
          // Serial.print(i);
          if ((PinArray[i] != -1) ) {
            //Serial.println("");
          //  Serial.println(PingTimeX[i]);
            if (PingSamplesX[i] > 0) {
              
            //  Serial.println(PingSamplesX[i]);
            //  Serial.println("\t");
            //  Serial.println(PingTimeX[i]);
              PingTime[i] =  (unsigned long) (PingTimeX[i] / PingSamplesX[i]); // average
              Measurements[Sensor] = (float) (microsecondsToCentimeters(PingTime[i]));
            }
            PingTimeX[i] = 0;
            PingSamplesX[i] = 0;
            Sensor++;
          }
        }
        SampleCt = 0;
        SampleAge[i] = micros();
      }

      DelayLatch = true;
      SampleCt++;
      PingTrigger(TriggerPin); // Send another ping
      zTimer = Timer;
    }
  }
}

float microsecondsToInches(long microseconds)
{
  return (float) microseconds / 74 / 2;
}

float microsecondsToCentimeters(long microseconds)
{
  return (float)microseconds / 29 / 2;
}


void SendSerialData()
{
  SerialEvent = false;
  int val = Serial.read() - '0';
  while (Serial.available())Serial.read();
  if (val == 0) {
    DataMessage();
    Serial.write(dataB, 30); // respond with message
  } else  if (val == 1) {
    RadarOut( DegSpread);
    Serial.println();
  }
}


uint8_t DataMessage() {
  int x = 0;
  for (int i = 0; i < 15; i++ ) {
    dataB[x] = (uint8_t)(((uint8_t)Measurements[i]) >> 8);
    x++;
    dataB[x] = (uint8_t)Measurements[i];
    x++;
  }
}

// port change Interrupt
ISR(PCINT0_vect) { //this ISR pins 8~13
  static uint8_t pin;
  static unsigned long cTime;
  cTime = micros();         // micros() return a uint32_t
  pin = PINB; //  get the state of all pins bit 0 = pin 8, bit 1 = pin 9 dtc.
  ToCompleteCtr++;
  sei();                    // re enable other interrupts
  CheckTimers(8, 13,  1, pin, cTime);
}

ISR(PCINT1_vect) { //this ISR s A0~A5
  static uint8_t pin;
  static unsigned long cTime;
  cTime = micros();         // micros() return a uint32_t
  pin = PINC; //  get the state of all pins bit 0 = pin 8, bit 1 = pin 9 dtc.
  ToCompleteCtr++;
  sei();                    // re enable other interrupts
  CheckTimers(14, 19,  2, pin, cTime);
}

ISR(PCINT2_vect) { //this ISR pins 0-7
  static uint8_t pin;
  static unsigned long cTime;
  cTime = micros();         // micros() return a uint32_t
  pin = PIND; //  get the state of all pins bit 0 = pin 8, bit 1 = pin 9 dtc.
  ToCompleteCtr++;
  sei();                    // re enable other interrupts
  CheckTimers(0, 7,  0, pin, cTime);
}

void CheckTimers(uint8_t StartPin, uint8_t EndPin, uint8_t group, uint8_t  pin, unsigned long cTime )
{
  volatile uint16_t dTime;
  mask[group] = pin ^ PCintLast[group];
  PCintLast[group] = pin;        
  for (uint8_t ii = 0; ii <= (EndPin - StartPin); ii++) {
    if (mask[group] >> ii & 1) { // pin has changed
      if ((pin >> ii & 1))edgeTime[(ii + StartPin)] = cTime; 
      else { // Pulse Went low calculate the duratoin
        dTime = cTime - edgeTime[(ii + StartPin)]; // Calculate the change in time
        PingTimeX[(ii + StartPin)] = PingTimeX[(ii + StartPin)] + dTime; 
        PingSamplesX[(ii + StartPin)]++;
      }
    }
  }
  ToCompleteCtr--;  //when all interupts are complete this will return to zero
}

// Install Pin change interrupt for a pin, can be called multiple times
void pciSetup(byte pin)
{
  if (pin <= 7)PinMask[0] =  bitWrite(PinMask[0], pin, 1); // PIND for pins 0~7
  else if (pin > 13) PinMask[2] =  bitWrite(PinMask[2] , pin - 14, 1); // PINC for A0~A5 Starts on Pin 14
  else PinMask[1] =  bitWrite(PinMask[1] , pin - 8, 1); // PINB for pins 8~13
  pinMode(pin, INPUT);// enable interrupt for pin...
  *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
  PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
  PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
  PinArray[pin] = 1;
  PinList[SensorCount] = pin;
  SensorCount++;
}

void serialEvent() {
  SerialEvent = true;
}

this is my setup
each echo pin goes to a unique input and pin 2 is tied to all trigger pins
all triggers activate at once

my code can handle multiple pin changes at the same time and log the start time. when each echo pin restores the interrupts trigger again and store the end time giving the trip distance.
amazingly accurate. my setup uses 8 sensors I can't see why you couldn't use more you can attach to any pin including analog for example if you want to use analog pin 3 unremark: pciSetup(17); // A3

data is stored in an array available for you to access at will. and this is a non blocking script so other code can run at the same time.

1 Like

DaveEvans:
Well, you could buy one. Or try a diode OR gate if you have a couple of diodes and a resistor.

I guess something like this?

I have a couple of diodes and resistors lying around, I might actually try that.

zhomeslice:
I have succeeded in triggering all at the same time using interrupts :slight_smile:
...
each echo pin goes to a unique input and pin 2 is tied to all trigger pins
all triggers activate at once

I was thinking about doing it like but, but I was a bit afraid of getting weird results if the echo from sensor A ends up in the receiver for sensor B. Do you notice any issues with crosstalk between the sensors?

sparknburn:
I guess something like this?

I have a couple of diodes and resistors lying around, I might actually try that.

I was thinking about doing it like but, but I was a bit afraid of getting weird results if the echo from sensor A ends up in the receiver for sensor B. Do you notice any issues with crosstalk between the sensors?

So lets look at what could change with the wiring that may resolve you concern
If we were to hook the first sensor trigger pin to the arduino pin 2 as I suggested but hook the second sensor trigger pin to the echo pin of first sensor. continue this for each sensor 3's trigger to 2's echo, 4's trigger to 3's echo.

Datasheet: https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf
The trigger pin only fires after the pulse to it falls the pulse has a minimum "Using IO trigger for at least 10us high level signal" but I didn't see a maximum limit on the datasheet. so the above method would fire each sensor in sequence. and you timing of the rise an fall of the echo pin is what you would use to detect distance. using your simple diode circuit you could achieve multiple sensors with 1 trigger pin and 1 input pin.

.........................

The more I type the more I'm still favoring the all at once method because the actual sensor only senses a 15° window. meaning that any pulse that is able to reflect back would be a indication of an object that is within 15° . Since the start pulse is exactly the same on all devices the result is more accurate even if it is a reflection of a reflection is detected (something is there but its just stealthy) that actually returns a signal. My thought is that this is more like a submarine that sends out 1 pulse and listens in all directions for each resulting echo if 2 sensors pick up the same echo you can triangulate the exact position on a flat plane

Z

Here is some simple code I put together for the above circuit :slight_smile: i created (did not test)

#define EchoInputCount 3
#define TriggerPin  2
// echo pin will be interrupt 1 on pin 3
#define DelayBetweenPings 50 // it works to about 5 milliseconds between pings

volatile  unsigned long PingTime[EchoInputCount];
volatile int Counter = 0;
volatile  unsigned long edgeTime;
volatile  uint8_t PCintLast;
int PinMask = B1000; // pin 3
float Measurements[EchoInputCount];

void PintTimer( )
{
  uint8_t pin;
  static unsigned long cTime;
  cTime = micros();         // micros() return a uint32_t
  pin = PIND >> 3 & 1;      // Quickly get the state of  pin 3
  if (pin)edgeTime = cTime; //Pulse went HIGH store the start time
  else { // Pulse Went low calculate the duratoin
    PingTime[Counter % EchoInputCount] = cTime - edgeTime; // Calculate the change in time  NOTE: the "% EchoInputCount" prevents the count from overflowing the array look up % remainder calculation
    Counter++;
  }
}
void debug() 
{
  char S[20];
  static unsigned long PingTimer;
  if ((unsigned long)(millis() - PingTimer) >= 100) {
    PingTimer = millis();
    for(int c = 0; c < EchoInputCount; c++){
      Serial.print(dtostrf(Measurements[c], 6, 1, S));
    }
    Serial.println();
  }
}

float microsecondsToInches(long microseconds) 
{
  return (float) microseconds / 74 / 2;
}

float microsecondsToCentimeters(long microseconds) 
{
  return (float)microseconds / 29 / 2;
}


void PingTrigger(int Pin)
{
  Counter = 0;
  digitalWrite(Pin, LOW);
  delayMicroseconds(1);
  digitalWrite(Pin, HIGH); // Trigger another pulse
  delayMicroseconds(10);
  digitalWrite(Pin, LOW);
}

void PingIt()
{
  unsigned long PT[EchoInputCount];
  static unsigned long PingTimer;
  if ((unsigned long)(millis() - PingTimer) >= DelayBetweenPings) {
    PingTimer = millis();
    cli ();         // clear interrupts flag
    for(int c = 0; c < EchoInputCount; c++){
       PT[c] = PingTime[c];
    }
    sei ();         // set interrupts flag
    for(int c = 0; c < EchoInputCount; c++){
         Measurements[c] = (float) (microsecondsToCentimeters(PT[c]));
    }
    //      Measurements = (float) (microsecondsToInches(PT));
    PingTrigger(TriggerPin); // Send another ping
  }
}

void setup()
{
  Serial.begin(115200);
  Serial.println("Ping Test");
  pinMode(3, INPUT);
  pinMode(2, OUTPUT);
  attachInterrupt(1, PintTimer, CHANGE );
}

void loop() 
{
  PingIt(); // Manage ping data
  debug(); // display ping data (at a slower rate due to serial limitations)
}

Working code with 4 Sensors into 1 input using this schematic as a guide



#define EchoInputCount 4
#define TriggerPin  2
// echo pin will be interrupt 1 on pin 3
#define DelayBetweenPings 50 // it works to about 5 milliseconds between pings

volatile  unsigned long PingTime[EchoInputCount];
volatile int Counter = EchoInputCount;
volatile  unsigned long edgeTime;
volatile  uint8_t PCintLast;
int PinMask = B1000; // pin 3
float Measurements[EchoInputCount];
unsigned long TimeoutTimer;
void PintTimer( )
{
  uint8_t pin;
  static unsigned long cTime;
  cTime = micros();         // micros() return a uint32_t
  pin = PIND >> 3 & 1;      // Quickly get the state of  pin 3
  if (pin)edgeTime = cTime; //Pulse went HIGH store the start time
  else { // Pulse Went low calculate the duratoin
    PingTime[Counter % EchoInputCount] = cTime - edgeTime; // Calculate the change in time  NOTE: the "% EchoInputCount" prevents the count from overflowing the array look up % remainder calculation
    Counter++;
  }
}
void debug()
{
  char S[20];
  static unsigned long PingTimer;
  if ((unsigned long)(millis() - PingTimer) >= 1) {
    PingTimer = millis();
    for (int c = 0; c < EchoInputCount; c++) {
      Serial.print(dtostrf(Measurements[c], 6, 1, S));
    }
    Serial.println();
  }
}

float microsecondsToInches(long microseconds)
{
  return (float) microseconds / 74 / 2;
}

float microsecondsToCentimeters(long microseconds)
{
  return (float)microseconds / 29 / 2;
}

void PingTrigger(int Pin)
{

  digitalWrite(Pin, LOW);
  delayMicroseconds(1);
  digitalWrite(Pin, HIGH); // Trigger another pulse
  delayMicroseconds(10);
  digitalWrite(Pin, LOW);
}

void PingIt()
{
  unsigned long PT[EchoInputCount];
  static unsigned long PingTimer;
  if (Counter >= EchoInputCount) {
    if ( ((unsigned long)(millis() - PingTimer) >= DelayBetweenPings)) {
      PingTimer = millis();
      cli ();         // clear interrupts flag
      for (int c = 0; c < EchoInputCount; c++) {
        PT[c] = PingTime[c];
      }
      sei ();         // set interrupts flag
      for (int c = 0; c < EchoInputCount; c++) {
        if (PT[c] < 23200) Measurements[c] = (float) (microsecondsToCentimeters(PT[c]));
      }
      //      Measurements = (float) (microsecondsToInches(PT));
      debug();
      delay(10);
      PingTrigger(TriggerPin); // Send another ping
      Counter = 0;
      TimeoutTimer = millis();

    }
  }
}

void setup()
{
  Serial.begin(115200);
  Serial.println("Ping Test");
  pinMode(3, INPUT);
  pinMode(2, OUTPUT);
  attachInterrupt(1, PintTimer, CHANGE );

}

void loop()
{
  PingIt(); // Manage ping data

  if ( ((unsigned long)(millis() - TimeoutTimer) >= 1000)) {
    PingTrigger(TriggerPin); // Send another ping
    Counter = 0;
    TimeoutTimer = millis();
  }

}

I worked on a solution for this myself very similar to what you described in the first post. I initiated the trigger through a pin expander chip (PCF8574) and muxed the results through an 8 way OR gate. Unfortunately some sensors out there never release the Echo ping when they don't see a response so they lock up the whole setup. I got around this by running the echoes through tri-state buffers which are turned on individually by holding the Trig down for 50ms - the trigger actuates on the falling edge, holding it down longer that 10us is not harmful. Timing on I2C is too slow/inaccurate for measurement, so the echo signal goes through a hardware interrupt ping and timing is measured asynchronously using interrupt callbacks.

This is documented at HC-SR04 I2C Octopus "octosonar" | Hackaday.io

Code at GitHub - arielnh56/OctoSonar: Arduino library supports many HC-SR04 sensors via I2C bus and hardware interrupt

It's all open sourced for DIY, or completed 8 port and 16 port boards on Tindie https://www.tindie.com/stores/arielnh56/

arielnh56:
I worked on a solution for this myself very similar to what you described in the first post. I initiated the trigger through a pin expander chip (PCF8574) and muxed the results through an 8 way OR gate. Unfortunately some sensors out there never release the Echo ping when they don't see a response so they lock up the whole setup. I got around this by running the echoes through tri-state buffers which are turned on individually by holding the Trig down for 50ms - the trigger actuates on the falling edge, holding it down longer that 10us is not harmful. Timing on I2C is too slow/inaccurate for measurement, so the echo signal goes through a hardware interrupt ping and timing is measured asynchronously using interrupt callbacks.

This is documented at HC-SR04 I2C Octopus "octosonar" | Hackaday.io

Code at GitHub - arielnh56/OctoSonar: Arduino library supports many HC-SR04 sensors via I2C bus and hardware interrupt

It's all open sourced for DIY, or completed 8 port and 16 port boards on Tindie https://www.tindie.com/stores/arielnh56/

Interesting and different. The versions I have done required little to no extra hardware.
I have done several versions of the HC-SR04 ping sensor. The inline one is a way to use 2 pine with 1 for input and 1 for pint initialization allowing for multiple ping sensors I am not sure of a limiting factor. I could see 20 to 30 HC-SR04's. The drawback is that it takes some time waiting for the timeout when no ping is detected on the HC-SR04 before triggering the next HC-SR04.

My favorite by far is this one where I have code allowing for any of the Inputs to take a reading at any time. The ping is generated at the same time for all HC-SR04 sensors. and the timing occurs when each input pin rises and again when it falls. I have enabled interrupts on all pins associated with each HC-SR04 this could handle up to 17 HC-SR04's on clone pro Mini with 7 Analog inputs but I have only tested it with 8 HC-SR04's. The accuracy and speed are amazing!

Unfortunately some sensors out there never release the Echo ping when they don't see a response so they lock up the whole setup.

I got around this by setting up each input with its own hardware interrupt as well as I repeatedly pulse the trigger on every HC-SR04 every 50ms. If the echo hasn't returned nothing happens the trigger is ignored.
because each input is connected to its own HC-SR04 the timing is based on the rise and fall of that input and not on the trigger pulse so in reality the trigger pulse could be generated by a 555 timer or at random the trigger pulse is irrelevant to the timing it only necessary to get things started.
so with every pin an interrupt input

This is older Version of my code and is better suited for this forum. It is not as complex as my class version.
This version does wait till all HC-SR04's have restored but since they all trigger at the same time the delay is usually minimal.
See Attached File
Z

UltrasoundPingForForum.ino (8.19 KB)

@zhomeslice sorry noob question, im just trying to understand your code.
In regards to your 1st set up, when i try it on my Arduino Nano, it constantly returns 0 for all sensors. From what i can see this is because i'm not getting the AllClear() to allow for the measurement.
Other then me manually inputting a 1 value from the serial monitor is there something else i have missed to get this to work?

I currently have three HC-SR04 sensors hooked up across 5v with echos on pin 3, 4 and 5 and the triggers all from pin 2.

Thanks in advance

"because each input is connected to its own HC-SR04 the timing is based on the rise and fall of that input and not on the trigger pulse"

In my octosonar the timing is also based on the rise and fall of the echo. The 50ms low to each trigger in turn both initiates the sequence on the selected sensor and gives the echo signal exclusive signalling to the single hardware interrupt pin. This also allows it to move on automatically to the next sensor without waiting for slow or hung sensors to time out.

arielnh56:
This also allows it to move on automatically to the next sensor without waiting for slow or hung sensors to time out.

Or you could use the timeout value of the pulseIn() function.

wvmarle:
Or you could use the timeout value of the pulseIn() function.

While I like the simplicity of the pulse in function, it is a blocking function and doesn't allow my robot to monitor for objects and receiving pulse commands from my RC Remote making it useless. Using interrupts to sample ping durations eliminates this issue.

Pulse in code.

unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout)
{
	// cache the port and bit of the pin in order to speed up the
	// pulse width measuring loop and achieve finer resolution.  calling
	// digitalRead() instead yields much coarser resolution.
	uint8_t bit = digitalPinToBitMask(pin);
	uint8_t port = digitalPinToPort(pin);
	uint8_t stateMask = (state ? bit : 0);

	unsigned long startMicros = micros();

	// wait for any previous pulse to end
	while ((*portInputRegister(port) & bit) == stateMask) {
		if (micros() - startMicros > timeout)
			return 0;
	}

	// wait for the pulse to start
	while ((*portInputRegister(port) & bit) != stateMask) {
		if (micros() - startMicros > timeout)
			return 0;
	}

	unsigned long start = micros();
	// wait for the pulse to stop
	while ((*portInputRegister(port) & bit) == stateMask) {
		if (micros() - startMicros > timeout)
			return 0;
	}
	return micros() - start;
}

Z

I don't see any interrupts in that snippet.

wvmarle:
I don't see any interrupts in that snippet.

Earlier posts in this post:
This uses 2 pins one to initiate trigger and one to read each sensor in sequence using interrupts. a diode is placed in line to isolate the sensing pin from the neighboring trigger pin allowing the prior HC-SR04 to trigger its neighbor.
http://forum.arduino.cc/index.php?topic=494594.msg3468757#msg3468757


Code:

#define EchoInputCount 4
#define TriggerPin  2
// echo pin will be interrupt 1 on pin 3
#define DelayBetweenPings 50 // it works to about 5 milliseconds between pings

volatile  unsigned long PingTime[EchoInputCount];
volatile int Counter = EchoInputCount;
volatile  unsigned long edgeTime;
volatile  uint8_t PCintLast;
int PinMask = B1000; // pin 3
float Measurements[EchoInputCount];
unsigned long TimeoutTimer;
void PintTimer( )
{
  uint8_t pin;
  static unsigned long cTime;
  cTime = micros();         // micros() return a uint32_t
  pin = PIND >> 3 & 1;      // Quickly get the state of  pin 3
  if (pin)edgeTime = cTime; //Pulse went HIGH store the start time
  else { // Pulse Went low calculate the duratoin
    PingTime[Counter % EchoInputCount] = cTime - edgeTime; // Calculate the change in time  NOTE: the "% EchoInputCount" prevents the count from overflowing the array look up % remainder calculation
    Counter++;
  }
}
void debug()
{
  char S[20];
  static unsigned long PingTimer;
  if ((unsigned long)(millis() - PingTimer) >= 1) {
    PingTimer = millis();
    for (int c = 0; c < EchoInputCount; c++) {
      Serial.print(dtostrf(Measurements[c], 6, 1, S));
    }
    Serial.println();
  }
}

float microsecondsToInches(long microseconds)
{
  return (float) microseconds / 74 / 2;
}

float microsecondsToCentimeters(long microseconds)
{
  return (float)microseconds / 29 / 2;
}

void PingTrigger(int Pin)
{

  digitalWrite(Pin, LOW);
  delayMicroseconds(1);
  digitalWrite(Pin, HIGH); // Trigger another pulse
  delayMicroseconds(10);
  digitalWrite(Pin, LOW);
}

void PingIt()
{
  unsigned long PT[EchoInputCount];
  static unsigned long PingTimer;
  if (Counter >= EchoInputCount) {
    if ( ((unsigned long)(millis() - PingTimer) >= DelayBetweenPings)) {
      PingTimer = millis();
      cli ();         // clear interrupts flag
      for (int c = 0; c < EchoInputCount; c++) {
        PT[c] = PingTime[c];
      }
      sei ();         // set interrupts flag
      for (int c = 0; c < EchoInputCount; c++) {
        if (PT[c] < 23200) Measurements[c] = (float) (microsecondsToCentimeters(PT[c]));
      }
      //      Measurements = (float) (microsecondsToInches(PT));
      debug();
      delay(10);
      PingTrigger(TriggerPin); // Send another ping
      Counter = 0;
      TimeoutTimer = millis();

    }
  }
}

void setup()
{
  Serial.begin(115200);
  Serial.println("Ping Test");
  pinMode(3, INPUT);
  pinMode(2, OUTPUT);
  attachInterrupt(1, PintTimer, CHANGE );

}

void loop()
{
  PingIt(); // Manage ping data

  if ( ((unsigned long)(millis() - TimeoutTimer) >= 1000)) {
    PingTrigger(TriggerPin); // Send another ping
    Counter = 0;
    TimeoutTimer = millis();
  }

}

This one uses one pin per sensor (8 pins for my example) and one to initiate the trigger pin for all sensors:
http://forum.arduino.cc/index.php?topic=494594.msg3374836#msg3374836
code:
UltrasoundPingForForum.ino

as you see both of my examples that I've posted here use interrupts. The first one is simplest by far! but it is the slowest the second one I have a version of the code the fires the trigger pin repeatedly without waiting for all the sensors to return interesting enough this works well as the trigger pin doesn't reset the HC-SR04 ping timer allowing further distance readings to update at there own leisure while the closer ones get accurate readings.
With this said I didn't account for echoes from neighboring sensors or any other stray noise. In my tests, it worked well as I am only interested in close items and could care less about something further away that created a false echo from another sensor.

Z

I followed your setup however the serial monitor shows 0 CM for all of my sensors :confused: . I should be seeing instantaneous readings when I move closer and away from sensors right? Attached is my circuit setup as well. The left most sensor echo goes to pin 10, middle one goes to 9, and right one goes to 3. Trigger from left one goes to trigger in middle, then goes middle trigger to right trigger, and finally right trigger goes to pin 2. In the code, I un-commented pcisetup 3, 9, and 10 like so:

.
.
.
// pciSetup(2); //This is my trigger pin
pciSetup(3);
// pciSetup(4);
// pciSetup(5);
// pciSetup(6);
// pciSetup(7);
// pciSetup(8);
pciSetup(9);
pciSetup(10);
// pciSetup(11); // SPI communications
.
.
.

I think my setup looks fine according to your explanations unless I'm missing a step?

serial monitor 1.jpg

serial monitor 2.jpg

asterixion92:
I followed your setup however the serial monitor shows 0 CM for all of my sensors :confused: . I should be seeing instantaneous readings when I move closer and away from sensors right? Attached is my circuit setup as well. The left most sensor echo goes to pin 10, middle one goes to 9, and right one goes to 3. Trigger from left one goes to trigger in middle, then goes middle trigger to right trigger, and finally right trigger goes to pin 2. In the code, I un-commented pcisetup 3, 9, and 10 like so:

.
.
.
// pciSetup(2); //This is my trigger pin
pciSetup(3);
// pciSetup(4);
// pciSetup(5);
// pciSetup(6);
// pciSetup(7);
// pciSetup(8);
pciSetup(9);
pciSetup(10);
// pciSetup(11); // SPI communications
.
.
.

I think my setup looks fine according to your explanations unless I'm missing a step?

It does look correct.
so how this works is all 3 of your HC-SR04 trigger pins are tied together and are fired by pin 2
The echo returns from the nearest objects to the sensors. since we can set interrupts on all pins, not just 2 and 3 we can see the change happening on all devices at the same time.

To test this further you can manually trigger a ping by pulsing each HC-SR04 individually. the resulting time from the last ping will be available.

Z

I have this newer code (attached) if that one is buggy... shouldn't be, it should work... but... try this one out it has i2c and serial capabilities I used the second UNO to control my bot receiving data from the 8 ping sensor uno.

UltrasoundPingAllAtOnceI2CxSerial.ino (7.79 KB)

master_reader_for_UltrasonicPingAllAtOnceI2C.ino (993 Bytes)

I have always just tied the trigger and echo pins together on the 4 pin sensors, and it works just as fine as if there was a single combined pin like on the 3 pin versions. Is there a long term issue that makes this a bad idea?

cmayer777:
I have always just tied the trigger and echo pins together on the 4 pin sensors, and it works just as fine as if there was a single combined pin like on the 3 pin versions. Is there a long term issue that makes this a bad idea?

That should work so you are triggering the pin then changing it to an input for the duration pulse from the echo pin?
the trigger pin ignores any further input till the echo pin completes its pulse. and there is a slight delay between the trigger and the echo pin going high. How would you start your timer for the echo pulse?

Hi.

I`m just wondering what value resister you used?

Thanks,
Greg.

gregsharp110:
Hi.

I`m just wondering what value resister you used?

Thanks,
Greg.

I just grabbed a 2.2K resistor but any resister in the 1K to 10K should work.
Z