Go Down

Topic: Multiple HC-SR04 with as few pins as possible (Read 134 times) previous topic - next topic

sparknburn

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.

DaveEvans

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.

zhomeslice

#2
Aug 13, 2017, 07:44 am Last Edit: Aug 13, 2017, 07:45 am by zhomeslice
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  :)
Code: [Select]
#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.
HC

sparknburn

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.

I have succeeded in triggering all at the same time using interrupts  :)
...
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?

zhomeslice

#4
Aug 13, 2017, 04:52 pm Last Edit: Aug 13, 2017, 05:23 pm by zhomeslice Reason: Adding code for circuit I designed
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 :) i created (did not test)
Code: [Select]

#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)
}

HC

zhomeslice

#5
Today at 03:36 am Last Edit: Today at 07:27 am by zhomeslice
Working code with 4 Sensors into 1 input using this schematic as a guide



Code: [Select]

#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();
  }

}




HC

Go Up