fairorgan:
I was actually only thinking of using two SR-04 sensors (at most three), but like your use of the array in the 15 sensor example for holding the data. Would I be better just duplicating the new ping_median once per sensor and not use an array nor interrupt method?
My project: I am controlling a robot’s head position via servos, which currently have either remote control (via joystick) or automatic random positioning of x and y axis. I would like to add the ability for close object tracking of the x axis, so thought the best way would be two ultrasonic sensors pointing slightly apart concealed on the head itself, and in the event of an object coming within say a couple of metres range, the head will stop random movements and track the object in x axis by moving the servo to keep the signal from both ultrasonic sensors the same, until either the object goes out of range or the robot gets bored.
If you just wanted to use an array to store the ping results, you could easily modify the 15 sensor sketch to work with ping_median instead of ping_timer. The problem is that the sketch would no longer be interrupt driven, and would be waiting for ping echos instead of doing anything else (like positioning the servo on the robot or doing probably much of anything else). For example, if you had 3 sensors and each sensor would take 5 samples to find the median that would be a total of 15 pings with at least a 29ms delay between each or a total of around 435ms (about 1/2 second) every ping cycle where your robot can do nothing but read the sensors. Not at all interrupt driven like the 15 sensor sketch where very little time is wasted monitoring the pings and you're free to do other things what seems like the same time.
A better solution is to continue to use the 15 sensor sketch as a base, but use oversampling to read the sensors multiple times, then average the results. Here's an example sketch:
#include <NewPing.h>
#define SENSORS 3 // Number or sensors.
#define ITERATIONS 5 // Iterations for each sensor.
#define PING_NUM ITERATIONS * SENSORS // Calculates the number of pings.
#define MAX_DISTANCE 200 // Maximum distance (in cm) to ping.
#define PING_INTERVAL 29 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).
unsigned long pingTimer[PING_NUM]; // Holds the times when the next ping should happen for each sensor.
unsigned int cm[PING_NUM]; // Where the ping distances are stored.
uint8_t currentSensor = 0; // Keeps track of which sensor is active.
NewPing sonar[PING_NUM] = { // Sensor object array (Each sensor's trigger pin, echo pin, and max distance to ping).
NewPing(12, 11, MAX_DISTANCE), // Sensor 0, sample 1
NewPing(2, 3, MAX_DISTANCE), // Sensor 1, sample 1
NewPing(4, 5, MAX_DISTANCE), // Sensor 2, sample 1
NewPing(12, 11, MAX_DISTANCE), // Sensor 0, sample 2
NewPing(2, 3, MAX_DISTANCE), // Sensor 1, sample 2
NewPing(4, 5, MAX_DISTANCE), // Sensor 2, sample 2
NewPing(12, 11, MAX_DISTANCE), // Sensor 0, sample 3
NewPing(2, 3, MAX_DISTANCE), // Sensor 1, sample 3
NewPing(4, 5, MAX_DISTANCE), // Sensor 2, sample 3
NewPing(12, 11, MAX_DISTANCE), // Sensor 0, sample 4
NewPing(2, 3, MAX_DISTANCE), // Sensor 1, sample 4
NewPing(4, 5, MAX_DISTANCE), // Sensor 2, sample 4
NewPing(12, 11, MAX_DISTANCE), // Sensor 0, sample 5
NewPing(2, 3, MAX_DISTANCE), // Sensor 1, sample 5
NewPing(4, 5, MAX_DISTANCE) // Sensor 2, sample 5
};
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 < PING_NUM; i++) // Set the starting time for each sensor.
pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;
}
void loop() {
for (uint8_t i = 0; i < PING_NUM; i++) { // Loop through all the sensors.
if (millis() >= pingTimer[i]) { // Is it this sensor's time to ping?
pingTimer[i] += PING_INTERVAL * PING_NUM; // Set next time this sensor will be pinged.
if (i == 0 && currentSensor == PING_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] = NO_ECHO; // Make distance NO_ECHO 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.
unsigned int uS[ITERATIONS], last;
uint8_t i, j, x, it, ii;
for (x = 0; x < SENSORS; x++) {
uS[0] = NO_ECHO;
it = ITERATIONS;
ii = 0;
for (i = 0; i < ITERATIONS; i++) {
last = cm[x + (i * SENSORS)];
if (last != NO_ECHO) { // Ping in range, include as part of median.
if (i > 0) { // Don't start sort till second ping.
for (j = ii; j > 0 && uS[j - 1] < last; j--) // Insertion sort loop.
uS[j] = uS[j - 1]; // Shift ping array to correct position for sort insertion.
} else j = 0; // First ping is starting point for sort.
uS[j] = last; // Add last ping to array in sorted position.
ii++; // Next ping in insertion sort array.
} else it--; // Ping out of range, skip and don't include as part of median.
}
Serial.print(x);
Serial.print("=");
Serial.print(uS[it >> 1]);
Serial.print("cm ");
}
Serial.println();
}
The above sketch is 3 sensors with 5 times oversampling. Instead of just printing the results in the oneSensorCycle function, it does the same insertion sort/median calculation as NewPing does with the ping_median method. It pings each sensor (0,1,2) then repeats 5 times. For example, the ping order is: 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2. This sketch will also accommodate different oversampling (iterations). Just change the ITERATIONS define and sonar array to match.
The advantage is that this sketch is totally interrupt driven. So, the ATmega has all kinds of time to do other things. While pinging, every 24uS it checks for a ping echo, but the rest of the time your sketch can do other things (like rotate a servo, move, etc). Once a full 3 sensor, 5 oversample pass is made, it goes to the oneSensorCycle function where the median values for the 3 sensors are calculated, which is very quick and only takes about 30uS. Basically, the ATmega is free to do other things at the same time it's pinging. Just be sure to not use any delay statements in your sketches!
Tim