Fast serial output for multiple Ping )) Ultrasonic Sensors

Hi folks,

I am trying desperately to get 4 Ultrasonic Sensors to output to serial as fast as possible. The thing is when I use a single Ultrasonic Sensor I can output "in real time" once every 10ms with the first patch I will copy here.

After trying to multiply the code lines from this first sketch to adapt it to 4 sensors, I found a thread which suggested using arrays for multiple sensors (that I will copy here as a second sketch).

Well I tried multiplying the code lines, I tried the sketch with arrays, I tried stripping this second sketch, I even deleted the delays which I know is not such a good idea, all with no luck!

Is there a fix somewhere so as an arduino with multiple sensors would output to serial as fast as an arduino with only one sensor?

This is the typical sketch for outputing to serial from a single Ultrasonic Sensor

/* Ping))) Sensor
  
   This sketch reads a PING))) ultrasonic rangefinder and returns the
   distance to the closest object in range. To do this, it sends a pulse
   to the sensor to initiate a reading, then listens for a pulse 
   to return.  The length of the returning pulse is proportional to 
   the distance of the object from the sensor.
     
   The circuit:
    * +V connection of the PING))) attached to +5V
    * GND connection of the PING))) attached to ground
    * SIG connection of the PING))) attached to digital pin 7

   http://www.arduino.cc/en/Tutorial/Ping
   
   created 3 Nov 2008
   by David A. Mellis
   modified 30 Jun 2009
   by Tom Igoe
 
   This example code is in the public domain.

 */

// this constant won't change.  It's the pin number
// of the sensor's output:
const int pingPin = 7;

void setup() {
  // initialize serial communication:
  Serial.begin(9600);
}

void loop()
{
  // establish variables for duration of the ping, 
  // and the distance result in inches and centimeters:
  long duration, cm;

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

  // The same pin is used to read the signal from the PING))): 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(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);

  // convert the time into a distance
  cm = microsecondsToCentimeters(duration);
  

  Serial.print(cm);
  Serial.print(" cm");
  Serial.println();
  
  delay(10);
}


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;
}

This is the second sketch which is skipping a few "beats" even though I tried everything I could! It's for multiple Ultrasonic Sensors

//http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1236272388

int ultraSoundSignalPins[] = {7,8,9,12}; // Front Left,Front, Front Right, Rear Ultrasound signal pins
char *pingString[] = {"a "," b ", " c ", " d "}; // just something to print to indicate direction

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

void loop()
{
  unsigned long ultrasoundValue;
  for(int i=0; i < 4; i++)
  {
    ultrasoundValue = ping(i);
    Serial.print(pingString[i]);
    Serial.print(ultrasoundValue);   
    delay(10);
  }
  Serial.println();
  delay(10);
 }

//Ping function
unsigned long ping(int i)
{
  unsigned long echo;

  pinMode(ultraSoundSignalPins[i], OUTPUT); // Switch signalpin to output
  digitalWrite(ultraSoundSignalPins[i], LOW); // Send low pulse
  delayMicroseconds(2); // Wait for 2 microseconds
  digitalWrite(ultraSoundSignalPins[i], HIGH); // Send high pulse
  delayMicroseconds(5); // Wait for 5 microseconds
  digitalWrite(ultraSoundSignalPins[i], LOW); // Holdoff
  pinMode(ultraSoundSignalPins[i], INPUT); // Switch signalpin to input
  digitalWrite(ultraSoundSignalPins[i], HIGH); // Turn on pullup resistor
  echo = pulseIn(ultraSoundSignalPins[i], HIGH); //Listen for echo
  return (echo / 58.138); //convert to CM
}

As I said, I tried many things to make it work faster (but it still skips a few readings):

  • deleted char *pingString[] = {"a ","b ", "c ", "d "}; and anything related
  • changed return (echo / 58.138); to return (echo); as to not convert to centimeters on the arduino and even deleted it
  • decreased the two delays in the void loop (even lower than 10 each) and even deleted the two delays

Nothing seems to work!

Any solutions are truly appreciated!
ygreq

use Serial.begin(115200) that makes the serial output 12 times as fast

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;
}

should not be used, just use : cm = duration/58; in your main code. It is not the main overhead but definitely faster...

Have you timed your ping(i) function, how many millis it takes?

Please try ping(i) as below : // just one lookup instead of looking it up every line of code

unsigned long ping(int i)
{
  uint8_t pin = ultraSoundSignalPins[i];  
  pinMode(pin, OUTPUT);    
  digitalWrite(pin, LOW);    
  delayMicroseconds(2);      
  digitalWrite(pin, HIGH);   
  delayMicroseconds(5);      
  digitalWrite(pin, LOW);    
  pinMode(pin, INPUT);      
  digitalWrite(pin, HIGH);  
  return pulseIn(pin, HIGH) /58.138;  //Listen for echo && convert to CM 
}

2 cents,
Rob

Hey Rob,

Thank you for your input.

  1. The thing is changing the Serial.begin doesn't help at all. I tried. It seems there is a limitation from within arduino and how it reads more than one sensor.
  2. About

just use : cm = duration/58; in your main code. It is not the main overhead but definitely faster...

. Thank you! I think it helps when using the sketch for the single sensor, but still I need speed improvements when using multiple sensors (so this doesn't help unfortunately with the second sketch).
3. About

Please try ping(i) as below : // just one lookup instead of looking it up every line of code

, I think what you are suggesting here is already in the second sketch I posted, the one using arrays.

I stripped the second sketch for multiple sensors even more. It outputs "duration" values, not distance. Still no results!

//http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1236272388

int ultraSoundSignalPins[] = {7,8,9,10}; // Front Left,Front, Front Right, Rear Ultrasound signal pins

void setup()
{

  Serial.begin(115200);
}

void loop()
{
  unsigned long ultrasoundValue;
  for(int i=0; i < 4; i++)
  {
    ultrasoundValue = ping(i);
    Serial.print(ultrasoundValue);
    Serial.print(" ");    
    delay(10);
  }
  Serial.println();
  delay(10);
 }

//Ping function
unsigned long ping(int i)
{
  unsigned long echo;

  pinMode(ultraSoundSignalPins[i], OUTPUT); // Switch signalpin to output
  digitalWrite(ultraSoundSignalPins[i], LOW); // Send low pulse
  delayMicroseconds(2); // Wait for 2 microseconds
  digitalWrite(ultraSoundSignalPins[i], HIGH); // Send high pulse
  delayMicroseconds(5); // Wait for 5 microseconds
  digitalWrite(ultraSoundSignalPins[i], LOW); // Holdoff
  pinMode(ultraSoundSignalPins[i], INPUT); // Switch signalpin to input
  digitalWrite(ultraSoundSignalPins[i], HIGH); // Turn on pullup resistor
  echo = pulseIn(ultraSoundSignalPins[i], HIGH); //Listen for echo
   
}

I still need desperate help!

I am looking at how fast the TX LED blinks when I have the first sketch for a single sensor and it blinks so fast that I can not see it go LOW. On the other hand, when I have the second sketch (for 4 sensors) .. well.. Let me just say, I see it blink! :slight_smile:

I think a solution would be for the arduino to send to serial one sensor info while calculating the second one and so on.

Any suggestions on how I can achieve this?

Thank you kindly!
ygreq

I did it!!

Real time, baby! :stuck_out_tongue:

Anyone wanting to improve the code, please do! But for now all I can say is IT WORKS! It is based on the first sketch I posted. It outputs duration and you have to do the calculations to find the distance in another program or just add the calculations yourselves! In this configuration, use routing in max 5 for example to check each sensor.

// this constant won't change.  It's the pin number
// of the sensor's output:
const int pingPin1 = 7;
const int pingPin2 = 8;
const int pingPin3 = 9;
const int pingPin4 = 10;

void setup() {
  // initialize serial communication:
  Serial.begin(9600);
}

void loop()
{
  // establish variables for duration of the ping, 
  // and the distance result in inches and centimeters:
  long duration1;
  pinMode(pingPin1, OUTPUT);
  digitalWrite(pingPin1, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin1, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin1, LOW);
  pinMode(pingPin1, INPUT);
  duration1 = pulseIn(pingPin1, HIGH);
  Serial.print("A ");
  Serial.println(duration1);
   
  long duration2;
  pinMode(pingPin2, OUTPUT);
  digitalWrite(pingPin2, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin2, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin2, LOW);
  pinMode(pingPin2, INPUT);
  duration2 = pulseIn(pingPin2, HIGH);
  Serial.print("B ");
  Serial.println(duration2);
  
  long duration3;
  pinMode(pingPin3, OUTPUT);
  digitalWrite(pingPin3, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin3, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin3, LOW);
  pinMode(pingPin3, INPUT);
  duration3 = pulseIn(pingPin3, HIGH);
  Serial.print("C ");
  Serial.println(duration3);
   
  long duration4;
  pinMode(pingPin4, OUTPUT);
  digitalWrite(pingPin4, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin4, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin4, LOW);
  pinMode(pingPin4, INPUT);
  duration4 = pulseIn(pingPin4, HIGH);
  Serial.print("D ");
  Serial.println(duration4);


 delay(10);
}

Hi, your code worked, but the readings that I get are jumping all around, from high to low, low to high every second. Is there anyway way to make it more stable or did you add a capacitor or something?

I added a 0.47uF capacitor in parallel to +5V and GND, a 100ohm resistor to +5V. Readings seems more stable but increases delay, making the readings inaccurate. Please advise.

ygreq:
I did it!!

Real time, baby! :stuck_out_tongue:

Anyone wanting to improve the code, please do! But for now all I can say is IT WORKS! It is based on the first sketch I posted. It outputs duration and you have to do the calculations to find the distance in another program or just add the calculations yourselves! In this configuration, use routing in max 5 for example to check each sensor.

// this constant won't change.  It's the pin number

// of the sensor's output:
const int pingPin1 = 7;
const int pingPin2 = 8;
const int pingPin3 = 9;
const int pingPin4 = 10;

void setup() {
  // initialize serial communication:
  Serial.begin(9600);
}

void loop()
{
  // establish variables for duration of the ping,
  // and the distance result in inches and centimeters:
  long duration1;
  pinMode(pingPin1, OUTPUT);
  digitalWrite(pingPin1, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin1, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin1, LOW);
  pinMode(pingPin1, INPUT);
  duration1 = pulseIn(pingPin1, HIGH);
  Serial.print("A ");
  Serial.println(duration1);
   
  long duration2;
  pinMode(pingPin2, OUTPUT);
  digitalWrite(pingPin2, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin2, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin2, LOW);
  pinMode(pingPin2, INPUT);
  duration2 = pulseIn(pingPin2, HIGH);
  Serial.print("B ");
  Serial.println(duration2);
 
  long duration3;
  pinMode(pingPin3, OUTPUT);
  digitalWrite(pingPin3, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin3, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin3, LOW);
  pinMode(pingPin3, INPUT);
  duration3 = pulseIn(pingPin3, HIGH);
  Serial.print("C ");
  Serial.println(duration3);
   
  long duration4;
  pinMode(pingPin4, OUTPUT);
  digitalWrite(pingPin4, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin4, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin4, LOW);
  pinMode(pingPin4, INPUT);
  duration4 = pulseIn(pingPin4, HIGH);
  Serial.print("D ");
  Serial.println(duration4);

delay(10);
}

Hi I tried your code. Only the last sensor gave an accurate reading. The first two is way off. Any advice on this? Here is my modified code:

// this constant won't change.  It's the pin number
// of the sensor's output:
const int pingPin1 = 2;
const int pingPin2 = 7;
const int pingPin3 = 8;
//const int pingPin4 = 10;

void setup() {
  // initialize serial communication:
  Serial.begin(9600);
}

void loop()
{
  // establish variables for duration of the ping, 
  // and the distance result in inches and centimeters:
  long duration1, cm;
  pinMode(pingPin1, OUTPUT);
  digitalWrite(pingPin1, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin1, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin1, LOW);
  pinMode(pingPin1, INPUT);
  duration1 = pulseIn(pingPin1, HIGH);
    cm = microsecondsToCentimeters(duration1);
  Serial.print("A ");
  Serial.print(cm);
    Serial.print("\t");    // prints a tab
    
  long duration2, cm2;
  pinMode(pingPin2, OUTPUT);
  digitalWrite(pingPin2, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin2, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin2, LOW);
  pinMode(pingPin2, INPUT);
  duration2 = pulseIn(pingPin2, HIGH);
    cm2 = microsecondsToCentimeters(duration2);
  Serial.print("B ");
  Serial.print(cm2);
    Serial.print("\t");    // prints a tab
  
  long duration3, cm3;
  pinMode(pingPin3, OUTPUT);
  digitalWrite(pingPin3, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin3, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin3, LOW);
  pinMode(pingPin3, INPUT);
  duration3 = pulseIn(pingPin3, HIGH);
    cm3 = microsecondsToCentimeters(duration3);
  Serial.print("C ");
  Serial.println(cm3);
   
 /* long duration4;
  pinMode(pingPin4, OUTPUT);
  digitalWrite(pingPin4, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin4, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin4, LOW);
  pinMode(pingPin4, INPUT);
  duration4 = pulseIn(pingPin4, HIGH);
  Serial.print("D ");
  Serial.println(duration4);
*/

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

Sorry timlee!

I just saw your replies.

@timlee and everyone else interested. I did not add any capacitors. The code just works for me. I just take the output coming out of the serial and use it in Max.

You can see a video of the installation where I used the sensors here. They were detecting how much the drawers were opened: http://kotkivisuals.com/site/lafarge-interactive-video-installation/

Good Evening together,

I have the exact problem like discussed above with the sensors, the reading is just to slow...

I build this code using Port Manipulation, but somehow I only get a 0 back.


long laenge;
long cm;

void setup ()
{
Serial.begin(9600);
DDRC = B00000010;
}

void loop()
{
PORTC = B00000000;
delayMicroseconds(2);
PORTC = B00000010;
delayMicroseconds(10);
PORTC = B00000000;
PINC = B00000001;

laenge = PINC;

cm = (laenge*34)/2000;

Serial.print(cm);
Serial.print("/n");
delay(500);
}

Do you guys know about that? Maybe someone has an idea to solve this problem?

Thanks

Nobody an idea how to solve this problem?

I think I know how to solve this but ut is not trivial.

You should start the ping ))) on all pins, preferably with direct port manipulation as
notoriou5 proposed.

but you should not use pulseIN for the receiving part as that is blocking code.
Instead you should use pinChangeInterrupts to get the timing of the 4 return signals.

if all 4 are set then one can convert timing to distance

in pseudocode

volatile uint32_t timing[4];

void loop()
{
  // clear timing;
  for (int i=0; i< 4; i++) timing[i] = 0UL;
  
  initializePCI( <pins> ); // to be investigated

  PORTC = pinMask;
  delayMicroseconds(s);


  // WAIT UNTIL READY
  bool ready = false;
  while (!ready)
  {
     ready = true;
     for (int i=0; i<4; i++) ready = ready && timing[i] != 0);
  }

  for (int i=0; i<4; i++) 
  {
    distance[i] = timing[i] /58;
    Serial.println(distance[i]);
  }
}

void PCinterrupt()
{
  pin = ....somecode...
  if (pin == HIGH) return; // wait for the LOW
  timing[pin] = micros();
}

something like that.

read this thread - PinChangeInt library- To attach interrupts to multiple Arduino (Uno/Mega) pins - Exhibition / Gallery - Arduino Forum -

latest code - Google Code Archive - Long-term storage for Google Code Project Hosting. - 2.30 beta

Hi robtillaart,

I was trying to work with your code but I couldn't understand completely. So I was trying my own one on the base of yours and the Internet. But somehow I my EventTime is Counting up instead of giving me the time of the pinchange. Is it possible to maybe only trigger High?? And how?? Would be great if you could look over this code of mine.
I only connected the Trigger to A0 and the Echo to A14.


#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

volatile long unsigned eventTime;
volatile long unsigned previousEventTime;
volatile long unsigned timeSinceLastEvent;
volatile long unsigned Abstand;
volatile byte portKstatus;
volatile byte eventFlag;

void setup ()
{
Serial.begin(9600);
DDRK = B00000000; //A8-15 INPUT
PORTK = B11111111; //HIGH
DDRF = B11111111; //A0-7 Output

sbi (PCICR,PCIE2);
sbi (PCMSK2, PCINT23);
sbi (PCMSK2, PCINT22);
sbi (PCMSK2, PCINT21);
sbi (PCMSK2, PCINT20);
}

void loop()
{
PINK = B00000000;
PORTF = B11111111;
delayMicroseconds(10);
PORTF = 00000000;
if (eventFlag == 1 && (eventTime-previousEventTime>100))
{
Abstand = (eventTime*34)/2000;
timeSinceLastEvent = eventTime - previousEventTime;
previousEventTime = eventTime;
Serial.print(eventTime);
Serial.print("ms");
Serial.print('\t');
Serial.print(timeSinceLastEvent);
Serial.print("ms");
Serial.print('\t');
Serial.print("Abstand:");
Serial.print(Abstand);
Serial.print('\t');
Serial.print(portKstatus,BIN);
Serial.print('\n');
eventFlag = 0;
eventTime = 0;
delay(1000);
}
}

ISR (PCINT2_vect)
{
portKstatus=PINK;

eventTime=millis();
eventFlag=1;
}


And thats the output:


101ms 101ms Abstand:1 10111111
1102ms 1001ms Abstand:18 11111111
2104ms 1002ms Abstand:35 11111111
3105ms 1001ms Abstand:52 11111111
4108ms 1003ms Abstand:69 11111111
5109ms 1001ms Abstand:86 11111111
6111ms 1002ms Abstand:103 11111111
7112ms 1001ms Abstand:120 11111111
8115ms 1003ms Abstand:137 11111111
9116ms 1001ms Abstand:154 11111111
10118ms 1002ms Abstand:172 11111111
11120ms 1002ms Abstand:189 10111111
12122ms 1002ms Abstand:206 11111111
13123ms 1001ms Abstand:223 11111111
14126ms 1003ms Abstand:240 11111111
15127ms 1001ms Abstand:257 11111111
16129ms 1002ms Abstand:274 11111111


Thanks a lot for your help :slight_smile: