NewPing Library: HC-SR04, SRF05, SRF06, DYP-ME007, Parallax PING))) - v1.7

rainierez:
Just used this on a project I was building against my digispark. I removed the timer stuff since I didn't need them and it wouldn't compile against it. But anyway. worked great and was better than me trying to futz with timing all day long. Thanks!!

I think the digispark uses the ATtiny85, that would make sense that it wouldn't work with the timer stuff. I believe I've modified my development code to detect the ATtiny processors and not use the timer interrupt stuff. The standard ping(), ping_cm(), ping_in(), ping_median() methods should still work just fine on the ATtiny.

If program space is also a problem with the limited memory on the ATtiny, you could reduce the compiled size by optimizing your sketch a little. Here's a suggestion:

#include <NewPing.h>

#define TRIGGER_PIN 3
#define ECHO_PIN 5
#define RED_PIN 0
#define GREEN_PIN 1
#define BLUE_PIN 4
#define MAX_DISTANCE 60

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

void setup() {
  pinMode(RED_PIN, OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(BLUE_PIN, OUTPUT);
}

void loop() {
  uint8_t distance = sonar.ping_cm();
  
  if (distance) {
    uint8_t greenValue = map(distance, 1, MAX_DISTANCE, 0, 255);  //Map the distance range to a range of 0 to 255 to the pwm value for the Green LED
    uint8_t redValue = 255 - greenValue;
    analogWrite(GREEN_PIN, greenValue);
    analogWrite(RED_PIN, redValue);
    digitalWrite(BLUE_PIN, LOW);
  } else {
    //Out of range, turn the LED blue
    digitalWrite(GREEN_PIN, LOW);
    digitalWrite(RED_PIN, LOW);    
    digitalWrite(BLUE_PIN, HIGH);
  }
  delay(50);
}

This would use quite a bit less sketch size and reduce the memory usage as well by using defines and right-sized variables.

On a side note, the digispark is under-powered and overpriced. For a couple dollars more you can get a Teensy 2.0 which is practically the same size and a full ATmega microcontroller so you have a ton of available pins (actually, it has more pins than an Arduino Uno). For just $3 more you can also get the Teensy 3.0 which is a 96 MHz 32-bit microcontroller, but still works with Arduino IDE.

Tim

teckel:

oigres666:

cyclegadget:

oigres666:
Hi everyone! I have a question... if I use 4 ultrasonic sensors and I want to send the data to Processing for transforming the data to OSC, can I use the 15 sensors sketch but with 4?? I don't know how can i do it...

#define SONAR_NUM       4 // Number of sensors.

yeah, I know that, but i want to know how to send to processing, How can processing read this data???.... coz from processing i can transform the data to OSC...or am I wrong?? :cold_sweat:
Thank u!

What do you mean by "i want to know how to send to processing"? What is "processing"? What is "OSC"?

The sketch pings the sensors and stores the values in the cm[] array. In the pingResult() function the contents of the cm[] array are output via serial. If you want to send that data to something else, whatever "processing" and "OSC" is, you would simply replace the contents of the pingResults() function with whatever you wanted to do with the results. We have no idea what you want to do with the results, but the data is there for you to do whatever you want with it. The pingResults() function is where you should do your magic with the results.

I have a post with help and alternative code for the 15 sensor sketch.

Tim

First of all, thank you for answering Tim! Processing is a language like, java, C++, etc... and OSC it's a protocol like MIDI for controlling a virtual instrument or whatever in your computer... My project is with 4 ultrasonic sensors to get the results and transform them into OSC so you can control eg. Ableton Live or whatever, moving your hands you can change a synth's parameters in real time.... Your code works perfectly for my 4 sensors! Now with your advice I'm going to try "play" with the data... Maybe I could obviate "Processing" and finding a library for Arduino which I can handle OSC... hahahaha sorry I'm rambling...
Anyway Thanks for ur code again!!!

I think the digispark uses the ATtiny85, that would make sense that it wouldn't work with the timer stuff. I believe I've modified my development code to detect the ATtiny processors and not use the timer interrupt stuff. The standard ping(), ping_cm(), ping_in(), ping_median() methods should still work just fine on the ATtiny.

If program space is also a problem with the limited memory on the ATtiny, you could reduce the compiled size by optimizing your sketch a little. Here's a suggestion:

#include <NewPing.h>

#define TRIGGER_PIN 3
#define ECHO_PIN 5
#define RED_PIN 0
#define GREEN_PIN 1
#define BLUE_PIN 4
#define MAX_DISTANCE 60

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

void setup() {
 pinMode(RED_PIN, OUTPUT);
 pinMode(GREEN_PIN, OUTPUT);
 pinMode(BLUE_PIN, OUTPUT);
}

void loop() {
 uint8_t distance = sonar.ping_cm();
 
 if (distance) {
   uint8_t greenValue = map(distance, 1, MAX_DISTANCE, 0, 255);  //Map the distance range to a range of 0 to 255 to the pwm value for the Green LED
   uint8_t redValue = 255 - greenValue;
   analogWrite(GREEN_PIN, greenValue);
   analogWrite(RED_PIN, redValue);
   digitalWrite(BLUE_PIN, LOW);
 } else {
   //Out of range, turn the LED blue
   digitalWrite(GREEN_PIN, LOW);
   digitalWrite(RED_PIN, LOW);    
   digitalWrite(BLUE_PIN, HIGH);
 }
 delay(50);
}




This would use quite a bit less sketch size and reduce the memory usage as well by using defines and right-sized variables.

On a side note, the digispark is under-powered and overpriced. For a couple dollars more you can get a Teensy 2.0 which is practically the same size and a full ATmega microcontroller so you have a ton of available pins (actually, it has more pins than an Arduino Uno). For just $3 more you can also get the Teensy 3.0 which is a 96 MHz 32-bit microcontroller, but still works with Arduino IDE.

Tim

Great stuff, I'm a hardware junky that knows enough code to be stupid with it. so seeing someone that knows how to make it effecient is alway a bonus. Thanks for cleaning that up a ton. I will have a go with that and let you know.

Again Thanks!

The new code you updated works great but if I leave the timer functions in the cpp then it won't compile. :confused: I get this in the IDE

...\Arduino\libraries\NewPing\NewPing.cpp: In static member function 'static void NewPing::timer_us(unsigned int, void (*)())':
...\Arduino\libraries\NewPing\NewPing.cpp:151: error: 'OCR2A' was not declared in this scope
...\Arduino\libraries\NewPing\NewPing.cpp:152: error: 'TIMSK2' was not declared in this scope
...\Arduino\libraries\NewPing\NewPing.cpp:152: error: 'OCIE2A' was not declared in this scope
...\Arduino\libraries\NewPing\NewPing.cpp: In static member function 'static void NewPing::timer_ms(long unsigned int, void (*)())':
...\Arduino\libraries\NewPing\NewPing.cpp:167: error: 'OCR2A' was not declared in this scope
...\Arduino\libraries\NewPing\NewPing.cpp:168: error: 'TIMSK2' was not declared in this scope
...\Arduino\libraries\NewPing\NewPing.cpp:168: error: 'OCIE2A' was not declared in this scope
...\Arduino\libraries\NewPing\NewPing.cpp: In static member function 'static void NewPing::timer_stop()':
...\Arduino\libraries\NewPing\NewPing.cpp:177: error: 'TIMSK2' was not declared in this scope
...\Arduino\libraries\NewPing\NewPing.cpp:177: error: 'OCIE2A' was not declared in this scope
...\Arduino\libraries\NewPing\NewPing.cpp: In static member function 'static void NewPing::timer_setup()':
...\Arduino\libraries\NewPing\NewPing.cpp:195: error: 'ASSR' was not declared in this scope
...\Arduino\libraries\NewPing\NewPing.cpp:195: error: 'AS2' was not declared in this scope
...\Arduino\libraries\NewPing\NewPing.cpp:196: error: 'TCCR2A' was not declared in this scope
...\Arduino\libraries\NewPing\NewPing.cpp:196: error: 'WGM21' was not declared in this scope
...\Arduino\libraries\NewPing\NewPing.cpp:197: error: 'TCCR2B' was not declared in this scope
...\Arduino\libraries\NewPing\NewPing.cpp:197: error: 'CS22' was not declared in this scope
...\Arduino\libraries\NewPing\NewPing.cpp:198: error: 'TCNT2' was not declared in this scope

rainierez:
The new code you updated works great but if I leave the timer functions in the cpp then it won't compile. :confused: I get this in the IDE

Yes, v1.5 of NewPing that you're running is not designed for the ATtiny. My development version now detects the ATtiny and automatically removes the timer stuff. In other words, you'll need to comment out the timer stuff in the library till I release version 1.6 of NewPing.

Tim

Hello,

I would like to ask if it is possibly to read the cm with decimal like 10.4 cm e.t.c.

Thank in advance.

Best regards,
Tasos

tasosstr:
Hello,

I would like to ask if it is possibly to read the cm with decimal like 10.4 cm e.t.c.

Thank in advance.

Best regards,
Tasos

Yes, simply use ping() which will return the milliseconds and then divide by whatever number you want to convert ms to cm (defaults to 57). The library uses integers to save program space (saves about 1k by avoiding floating point). Also, the accuracy isn't really better than a cm and there's other variables like temperature that will change the speed of sound and mess up your calculations.

However, keep in mind that doing any of this will really be of no good as the sensor is not accurate enough to actually give you a consistent reading of 10.4. If you did use a floating point number for ms to cm conversion, the ping results would probably jump around from 10.9 to 10.1, with everything still. So, I'm sure sure what use you'd really get from it. All you'd really be doing is wasting 1k of program space and getting a bunch of different readings and not knowing which one was right.

Basically, I'd suggest leaving it as-is and accepting that the sensor is really only accurate to 1cm or 1in. And, enjoy the extra speed and 1k of program space you saved by not using floating point calculations on things that are not accurate below an integer anyway. NewPing is designed to be small, fast, and accurate. Making it floating point would not help the accuracy and it would make it slower and larger. So, it would hurt the library in every way. But, you're free to use floating point if you wish by using the ping() method along with your own conversion (after also monitoring the temperature).

Let me know if you actually find this useful, I've yet to hear of it being at all useful or more accurate.

Tim

Well that's a good point, but it is still likely to jump around between 10 and 11 cm.

teckel:
Interesting! My ultrasonic sensors only use a couple mA each, while the Arduino Uno uses 60 mA (I'd guess the Mega uses more). I wouldn't think that just the sensors would put your USB or the voltage regulator over the top. Have you measured what kind of current it's using? It just seems very unlikely that it's within a couple mA of going over the top and the sensors draw enough current to do it. I guess it's possible, just doesn't seem likely.

Tim

Well it did it for me at least. Hopefully it helps someone else as well.

The operating voltage for the HC-SR04 should apperently be 4.50 to 5.0V. But I did some quick measurements which shows that that's not the case for my sensors:

With Ethernet shield:

  1. Power via USB port
    1.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.55 V
    1.2. Without LCD Screen: 4.60 V

  2. Power from power outlet
    2.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.98 V
    2.2. Without LCD Screen: 4.98 V

Without Ethernet shield:
3. Power via USB port
3.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.76 V
3.2. Without LCD Screen: 4.82 V

  1. Power from power outlet
    4.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.98 V
    4.2. Without LCD Screen: 4.98 V

i.e. since it doesn't work properly for me with just the sensors and the Ethernet shield connected (using just USB), over 4.60V seems to be needed.

michinyon:
Well that's a good point, but it is still likely to jump around between 10 and 11 cm.

Not if you turn off rounding. But, you are right, it can bounce between to different integers as well. However, that's because the sensor is not accurate to a fraction of a cm. It's not because the library doesn't do floating point. Basically, it's not helpful to do floating point math. But, feel free to do it. Replace "ping_cm()" with "ping()/57.0" and you'll get a decimal result. If you find this useful I'd really be amazed, but the library is designed so you can do it as shown.

Tim

gandalf50:

teckel:
Interesting! My ultrasonic sensors only use a couple mA each, while the Arduino Uno uses 60 mA (I'd guess the Mega uses more). I wouldn't think that just the sensors would put your USB or the voltage regulator over the top. Have you measured what kind of current it's using? It just seems very unlikely that it's within a couple mA of going over the top and the sensors draw enough current to do it. I guess it's possible, just doesn't seem likely.

Tim

Well it did it for me at least. Hopefully it helps someone else as well.

The operating voltage for the HC-SR04 should apperently be 4.50 to 5.0V. But I did some quick measurements which shows that that's not the case for my sensors:

With Ethernet shield:

  1. Power via USB port
    1.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.55 V
    1.2. Without LCD Screen: 4.60 V

  2. Power from power outlet
    2.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.98 V
    2.2. Without LCD Screen: 4.98 V

Without Ethernet shield:
3. Power via USB port
3.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.76 V
3.2. Without LCD Screen: 4.82 V

  1. Power from power outlet
    4.1. With LCD screen connected as well, at 3 HC-SR04 ultrasonic sensors: 4.98 V
    4.2. Without LCD Screen: 4.98 V

i.e. since it doesn't work properly for me with just the sensors and the Ethernet shield connected (using just USB), over 4.60V seems to be needed.

I've ran ultrasonic sensors down to 3.3 volts and they still work. I forgot I have an Internet shield, I'll see what kind of milliamps it uses.

Tim

teckel:

tasosstr:
Hello,

I would like to ask if it is possibly to read the cm with decimal like 10.4 cm e.t.c.

Thank in advance.

Best regards,
Tasos

Yes, simply use ping() which will return the milliseconds and then divide by whatever number you want to convert ms to cm (defaults to 57). The library uses integers to save program space (saves about 1k by avoiding floating point). Also, the accuracy isn't really better than a cm and there's other variables like temperature that will change the speed of sound and mess up your calculations.

However, keep in mind that doing any of this will really be of no good as the sensor is not accurate enough to actually give you a consistent reading of 10.4. If you did use a floating point number for ms to cm conversion, the ping results would probably jump around from 10.9 to 10.1, with everything still. So, I'm sure sure what use you'd really get from it. All you'd really be doing is wasting 1k of program space and getting a bunch of different readings and not knowing which one was right.

Basically, I'd suggest leaving it as-is and accepting that the sensor is really only accurate to 1cm or 1in. And, enjoy the extra speed and 1k of program space you saved by not using floating point calculations on things that are not accurate below an integer anyway. NewPing is designed to be small, fast, and accurate. Making it floating point would not help the accuracy and it would make it slower and larger. So, it would hurt the library in every way. But, you're free to use floating point if you wish by using the ping() method along with your own conversion (after also monitoring the temperature).

Let me know if you actually find this useful, I've yet to hear of it being at all useful or more accurate.

Tim

Hello Dear Tim,

Thank you very much for the reply and for the help ! ! !

You have make amazing work well done ! ! !

I was try the bellow example:

#include <NewPing.h>

#define TRIGGER_PIN  12  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     11  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 500 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

void setup() {
  Serial.begin(115200); // Open serial monitor at 115200 baud to see ping results.
}

void loop() {
  delay(50);                      // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings.
  float uS = sonar.ping(); // Send ping, get ping time in microseconds (uS).
  Serial.print("Ping: ");
  Serial.print(uS / US_ROUNDTRIP_CM); // Convert ping time to distance in cm and print result (0 = outside set distance range)
  Serial.println("cm");
}

and working perfect ! ! !

I need this because i want to get the height of a petrol tank, so now i can have more better results because of you :slight_smile:

Again Thank you Very Much ! ! !

Best Regards,
Tasos

THANKS FOR THIS NEW SONIC LIBRARY! GREAT WORK !!!!!!!!!!!!!!!!!!!

tasosstr:
I need this because i want to get the height of a petrol tank, so now i can have more better results because of you :slight_smile:

Tasos, just a word of caution before you get too far into your project:

Ultrasonic sensors don't work well with petrol/gasoline. I seem to recall it was due to vapor in the tank giving false readings.

My wife used to work for ESI in Wichita, KS, which specialized in designing and installing liquid storage tank level sensors. They provided a business-to-business link where their hardware would monitor tanks of, say, liquid chicken feed additive, and automatically call the tank-filling-company and schedule a delivery when the tank was low.

I recall my wife talking specifically about a big sale hanging on their ability to monitor fuel tank levels, and the lead engineer was under a lot of pressure to develop a sensor that worked, but a solution eluded him and it was causing a lot of friction at work. This was many years ago, and it's now a solved problem, but the problem still stands for generic ultrasonic sensors. (It may have been a solved problem then, and maybe the boss didn't want to use a third-party product.)

There are commercial sensors available for measuring petrol/gasoline, but I'm sure they're at least a magnitude more expensive than the hobby ultrasonic sensors.

Ultrasonic sensors don't work well with petrol/gasoline.

Was the problem simply one of calibration, i.e. the speed of sound in vapour-laden air vs. speed of sound in air?

I'm building a little robot with an HC-SR04 for obstacle avoidance, and I'm finding NewPing to be very nice. Thanks for your hard work, Tim.

I'm using NewPing in timer-mode, but I don't really need to do anything inside my echoCheck() function... I just want a distance value to be available to other parts of the code.

Here's where I run into a problem... I'm not seeing a good way to directly determine the difference between "timed out without an echo" and "still waiting for an echo" conditions. check_ping() returns false in both cases, and x.ping_result is only updated on receiving an echo. So x.ping_result always indicates the distance of the last successful ping, but it will always be a positive value even when pings are timing out without an echo.

So inside my echoCheck(), I have to assign the value of ping_result to a global variable, and then when I read the global variable elsewhere in the code, I have to remember to set it to zero. This is effectively doing the same job as ping_result, except it's happening in my code instead of inside NewPing.

Would it make sense for the timeout reset portion of check_ping() to set ping_result back to 0? ping_result isn't used anywhere else in NewPing, so it wouldn't affect internal behavior. I don't know how it might affect behavior of existing programs, but I suppose it could.

With this minor change, I could eliminate my global variable and just look at ping_result any time I want to know the status of the last ping.

On one level, I'd like the option for the timer-driven model to be even less involved. I'd like to start the "ping engine" in setup() and have it ping every 50 usec "forever" without calling ping_timer() again, and be able to just check ping_result any time I like. I can't call ping_timer(echoCheck) from within echoCheck(), because again, check_timer()'s return value doesn't differentiate between "timed out" and "still waiting". (And despite being a career developer / system architect, I'll admit that I may be missing something here and that the problem is easily solved.)

If check_timer() returned an int... -1 for "still waiting", 0 for "timed out" and a positive value (distance) for an "echo received", it would be a lot more flexible. And it would break existing code, since its' an API change.

I suppose I could peek at the timer value itself to see if it's still enabled, but having the library help with that would be ideal. Maybe a function or variable I could call/look at to find out why check_timer() returned false? Of course, an alternative function to ping_timer(), say, ping_forever(), that didn't (or optionally) require a echoCheck() and handled running the ping over and over on its own might be ideal. (And I'll admit, this is what I expected when I first dug into the timer-driven mode.)

I may make some tweaks to the library and see which ideas pan out.

AWOL:

Ultrasonic sensors don't work well with petrol/gasoline.

Was the problem simply one of calibration, i.e. the speed of sound in vapour-laden air vs. speed of sound in air?

A bit of searching suggests you may be on the right track, but it's apparently not as simple as a one-time calibration. The speed of sound changes with the density of the fuel vapor, which varies with temperature. If the only significant variable is temperature, knowing the temperature inside the tank would make compensation relatively simple. But the temperature at the sensor may not be representative of the rest of the tank, so may require a remote sensor.

It had an experienced sensor design engineer stumped, at least for awhile.

I asked my wife, and she said vapor was one issue, and another is that gasoline has a high thermal expansion ratio... to get an accurate volume measurement, you have to know the temperature of the fuel as well as its level. But that wouldn't be an issue if you're only concerned with "the tank's getting low". Apparently a float was a cheaper and more reliable method, at least at the time.

There's no provision in NewPing to track when a ping is in process. Once a ping is complete, you know if there was no ping or a ping and at what distance. It's designed to be like an interrupt, where once the ping is complete your code then processes the result. There's no requirement for the pingResult() function. That's just there as a sample because most people want to process results as soon as a ping has been completed. If you don't need it, simply delete it.

What you want to accomplish can be done without any modification to the library. As your task is specific and different than most, it's best to have this code be in your sketch instead of the library. This actually allows for more flexibility. The process of pinging every 50 milliseconds can easily be done with the 15 sensor sketch example. It doesn't matter if it's in your sketch or inside the NewPing library, it would be just as involved. Having this inside your sketch allows for exact control as well.

ping_forever() would be a function in your sketch, basically like the 15 sensor sketch. I've already done the work for you, you just need to impliment it in your sketch. Having the library do too much would be outside the scope of the library. It's designed to ping the sensor, not really manage all aspects of when a ping is sent or for how long. The 15 sensor sketch is your ping_forever() function. Just use that and add the rest of your code. Also, be sure to read my post on more details about the 15 sensor sketch, and ways to tweak it for your needs. I believe you'll find that I've already answered your questions there.

Here's a sample sketch that I whipped up for you that may assist:

#include <NewPing.h>

#define SONAR_NUM      3
#define MAX_DISTANCE 200
#define PING_INTERVAL 33

unsigned long pingTimer[SONAR_NUM];
unsigned int cm[SONAR_NUM];
uint8_t currentSensor = 0;
uint8_t pingStatus = 0;

NewPing sonar[SONAR_NUM] = {
  NewPing(4, 5, MAX_DISTANCE),
  NewPing(6, 7, MAX_DISTANCE),
  NewPing(8, 9, MAX_DISTANCE)
};

void setup() {
  Serial.begin(115200);
  pingForever();
}

void loop() {
  for (uint8_t i = 0; i < SONAR_NUM; i++) { // Loop through all the sensors.
    if (millis() >= pingTimer[i]) {
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;
      sonar[currentSensor].timer_stop();
      currentSensor = i;
      cm[currentSensor] = 0;
      pingStatus = 1;
      sonar[currentSensor].ping_timer(echoCheck);
    }
  }
  
  /*
  At any time, you can get the value of pingStatus, currentSensor,
  or read the values of the last pings in the cm[] array.  It's
  up to you to figure out what you want to do with this data.
  */
}

void pingForever() {
  pingTimer[0] = millis();
  for (uint8_t i = 1; i < SONAR_NUM; i++) pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;
}

void pingStop() {
  for (uint8_t i = 0; i < SONAR_NUM; i++) pingTimer[i] = -1;
  pingStatus = 0;
}

void echoCheck() {
  if (sonar[currentSensor].check_timer()) {
    cm[currentSensor] = sonar[currentSensor].ping_result / US_ROUNDTRIP_CM;
    pingStatus = 0;
  }
}

Beyond this, and the 15 sensor sketch help post, you'll need to post your sketch for me to offer suggestions.

Tim

I would also suggest a float because then the electronics can be placed outside the tank with only the float on the inside. Putting electronics inside fuel vapor doesn't sound like a good ideal. Actually, it sounds like a very bad idea. A short could result in a spark which results in a very bad day.

Tim

Hi everyone, I would like to ask if it's possible to hold a value, I mean, if I remove my hand from the sensor, then the sensor measure out of range, I would like that the last value sent for serial (usb) was the measure from my hand to the sensor before remove... This is my sketch which I found in this post, BTW really useful!!!

#include <NewPing.h>

#define SONAR_NUM     4 // Number or sensors.
#define MAX_DISTANCE 200 // Maximum distance (in cm) to ping.
#define PING_INTERVAL 33 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

unsigned long pingTimer[SONAR_NUM]; // Holds the times when the next ping should happen for each sensor.
unsigned int cm[SONAR_NUM];         // Where the ping distances are stored.
uint8_t currentSensor = 0;          // Keeps track of which sensor is active.

NewPing sonar[SONAR_NUM] = {     // Sensor object array.
  NewPing(12, 11, MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping.
  NewPing(10, 9, MAX_DISTANCE),
  NewPing(8, 7, MAX_DISTANCE),
  NewPing(6, 5, MAX_DISTANCE) 
};

void setup() {
  Serial.begin(115200);
  pingTimer[0] = millis() + 75;           // First ping starts at 75ms, gives time for the Arduino to chill before starting.
  for (uint8_t i = 1; i < SONAR_NUM; i++) // Set the starting time for each sensor.
    pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;
}

void loop() {
  for (uint8_t i = 0; i < SONAR_NUM; i++) { // Loop through all the sensors.
    if (millis() >= pingTimer[i]) {         // Is it this sensor's time to ping?
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;  // Set next time this sensor will be pinged.
      if (i == 0 && currentSensor == SONAR_NUM - 1) oneSensorCycle(); // Sensor ping cycle complete, do something with the results.
      sonar[currentSensor].timer_stop();          // Make sure previous timer is canceled before starting a new ping (insurance).
      currentSensor = i;                          // Sensor being accessed.
      cm[currentSensor] = 0;                      // Make distance zero in case there's no ping echo for this sensor.
      sonar[currentSensor].ping_timer(echoCheck); // Do the ping (processing continues, interrupt will call echoCheck to look for echo).
    }
  }
  // The rest of your code would go here.
  
}

void echoCheck() { // If ping received, set the sensor distance to array.
  if (sonar[currentSensor].check_timer())
    cm[currentSensor] = sonar[currentSensor].ping_result / US_ROUNDTRIP_CM;
}

void oneSensorCycle() { // Sensor ping cycle complete, do something with the results.
  for (uint8_t i = 0; i < SONAR_NUM; i++) {
      Serial.print(cm[i]);
}