Multiple HC-SR04's on an interrupt; only reading one

I'm working on a small network of robots (three of them) that work collaboratively to navigate an obstacle course. They use HC-SRO4s to read their environment, and when one finds a way forward, he signals to the other two to come over to him because he found an opening. They regroup on the other side and rinse and repeat.

Pic of bot: Imgur: The magic of the Internet

To avoid sensor interference between the three bots, I'm putting their ultrasonic sensors on a 300ms timer one interrupt, with each robot starting its timer 100ms after the last, allocating 100 ms to read its surroundings, and then waiting 200ms for its fellows to do the same. I'm using an interrupt so it can be doing it's complex driving and communication code and simply pause to update its ultrasonic values every 300ms. However, it's only reading the last sensor value called (in this case the left sensor) leading me to believe it's maybe over-writing the previous values or something? Pertinent code is below (driving and communication excluded for the sake of simplicity:

#include <NewPing.h>
#include <TimerOne.h>

//--------------------SENSORS-------------------- 
#define SONAR_NUM     4 // Number of sensors.
#define MAX_DISTANCE 200 // Maximum distance (in cm) to ping.
unsigned int cm[SONAR_NUM];         // Where the ping distances are stored.
uint8_t currentSensor = 0;          // Keeps track of which sensor is active.

volatile long Fecho_start = 0;          // Records start of echo pulse 
volatile long Fecho_end = 0;           // Records end of echo pulse
volatile long Fecho_duration = 0;    // Duration - difference between end and start

int fTrig = 4;
int fEcho = 5;
int rTrig = 6; 
int rEcho = 7;
int lTrig = 8;
int lEcho = 9;

NewPing frontSensor = NewPing(4, 5, MAX_DISTANCE); //front
NewPing rightSensor = NewPing(6, 7, MAX_DISTANCE); //right
NewPing leftSensor = NewPing(8, 9, MAX_DISTANCE); //left


void setup() {
  Timer1.initialize(300000);
  Timer1.attachInterrupt(sensnor);

  Serial.begin(9600);
  
  pinMode(fTrig, OUTPUT);
  pinMode(rTrig, OUTPUT);
  pinMode(lTrig, OUTPUT);
    
  pinMode(fEcho, INPUT);
  pinMode(rEcho, INPUT);
  pinMode(lEcho, INPUT);

}


void sensnor(){
  frontSensor.ping_timer(fEchoCheck);
  rightSensor.ping_timer(rEchoCheck);
  leftSensor.ping_timer(lEchoCheck);
}

void fEchoCheck() { // Timer2 interrupt calls this function every 24uS where you can check the ping status.
  if (frontSensor.check_timer()) { // This is how you check to see if the ping was received.
    cm[0] = frontSensor.ping_result / US_ROUNDTRIP_CM;
    Serial.print("Front: ");
    Serial.print(cm[0]); // Ping returned, uS result in ping_result, convert to cm with US_ROUNDTRIP_CM.
    Serial.println("cm");
  }
}



void rEchoCheck() { // Timer2 interrupt calls this function every 24uS where you can check the ping status.
  if (rightSensor.check_timer()) { // This is how you check to see if the ping was received.
    cm[1] = rightSensor.ping_result / US_ROUNDTRIP_CM;
    Serial.print("Right: ");
    Serial.print(cm[1]); // Ping returned, uS result in ping_result, convert to cm with US_ROUNDTRIP_CM.
    Serial.println("cm");
  }
}



void lEchoCheck() { // Timer2 interrupt calls this function every 24uS where you can check the ping status.
  if (leftSensor.check_timer()) { // This is how you check to see if the ping was received.
    cm[2] = leftSensor.ping_result / US_ROUNDTRIP_CM;
    Serial.print("Left: ");
    Serial.print(cm[2]); // Ping returned, uS result in ping_result, convert to cm with US_ROUNDTRIP_CM.
    Serial.println("cm");
  }
}

Any help would be greatly appreciated

EDIT: typos

Won't compile. No loop() function.

The NewPing library already handles reading the sensors on a schedule There is no reason to reinvent that wheel.

You cannot set 3 different interrupt functions to be called for 3 different sensors.

Check How they manage 15 sensors in the example. They poll each sensor one after the other, not all at the same time.

johnwasser:
Won't compile. No loop() function.

Forgot to include that when I was putting in the code, it's below:

void loop() {
uint8_t i;
Serial.print("Fcm: "); // prints to test the sensor vaules
Serial.print(fCm);
Serial.println();
Serial.print("Rcm: ");
Serial.print(rCm);
Serial.println();
Serial.print("Lcm: ");
Serial.print(lCm);
Serial.println();
Serial.println();
// Do drive & navigate stuff
}

J-M-L:
You cannot set 3 different interrupt functions to be called for 3 different sensors.

Check How they manage 15 sensors in the example. They poll each sensor one after the other, not all at the same time.

I have looked at that code, and messed around with an adaptation of their code, putting the delay between sensor readings at 0. This triggered the same result, leading me to believe that putting all the sensor readings on one interrupt with no delay was the issue, so I then adjusted it to put a 30 ms delay between readings for safety, and then putting in a 210 ms delay once it has cycled through all the sensors essentially giving a 300 ms delay [210 + (30ms * 3 sensors) = 300 ms].

I am pretty nooby at interrupts, so I am worried about the fact that it isn't on a timer one interrupt. My worry with the way they do the interrupt is that if my code doesn't make it back up to the check at the top of the loop in time (as is quite likely, for if the robot receives a communication from one of the other bots, it begins a lengthy method with several while loops that could easily take several full seconds).

Will this be a problem? If so, is there a way for me to do a similar thing with Timer 1? It was my understanding that I set the delay once in the setup for Timer 1, and never again, making it impossible for me to do the same trick I could do with the timer the NewPing15Sensors code uses.

You wrote

I have looked at that code, and messed around with an adaptation of their code, putting the delay between sensor readings at 0.

Indeed if you mean you set PING_INTERVAL at zero - Then there was no chance their code works at all

For example that line ensures sensors will be triggered in a sequence (at different increasing times)

pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;

But if PING_INTERVAL is zero then they are all set at the same value and in the loop

if (millis() >= pingTimer[i]) {
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;

You will get weird results as all sensors will qualify at the same time.

The approach they take is based on interrupts, but you need to return to the main loop to trigger a new read. If you block the loop for multiple seconds, then you are stuck.

Challenge if your communications takes many seconds and you perform sensors reads through interrupt is that the values you exchange (assuming this is what you send around) between your robots will change while you are communicating... Not great?

Can you explain at conceptual level how your robots are collaborating? Are they exchanging info from the distance sensors?

J-M-L:
Can you explain at conceptual level how your robots are collaborating? Are they exchanging info from the distance sensors?

They use the sensor data only for their own navigational purposes. Essentially, the three robots are lined up like this in an obstacle course: https://drive.google.com/a/esdallas.org/file/d/0BxPDWuF3G13lRS1yc0NLRUl5b0E/view

They move on their x-axis looking for an opening forward. When they find one, they send either a 0, 1, or a 2 depending on where they were originally positioned (left, middle, or right respectively), as seen in this image: https://drive.google.com/a/esdallas.org/file/d/0BxPDWuF3G13lMlRLSkFlUXJ1Nkk/view

The other two robots then head in the direction the signal came from (left for 0, towards the middle for 1, and right for 2) and head through the opening. They arrange themselves, and then reassign values depending on the new arrangement of the bots. Let's use the left robot as an example. The way the navigation works for him is that as long as the sensor in front reads less than 10 cm and the left sensor reads more than 10 cm, it heads to the left. If the left sensor reads less than 10 cm, it then goes to the right as long as the right sensor is greater than (you guessed it) 10 cm. If both values are exhausted, it then enters a failure state where it waits for another bot to find an opening (for the sake of testing, there will always be some way forward).

How do you synchronise the robots so that they don't send ranging pulses at the same time?

That's the point of the interrupt, and each one starts its interrupt on a delay. The disposition variable is 0, 1, or 2 depending on whether its the left, middle, or right robot respectively. Right before the Timer starts, there is a delay(100*disposition) so that they are offset by 100ms each.

Thank you all for your help, but I solved the problem! Turns out you can dynamically update a Timer 1 delay's period, so I'm using the following code for my interrupt (the sensor() method is attached to the interrupt, the echoCheck() is just something it references).

void sensnor(){
if (counter == 0 && currentSensor == SONAR_NUM - 1) Timer1.setPeriod(PING_INTERVAL); // 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 = counter;                    // Sensor being accessed.
sonar[currentSensor].ping_timer(echoCheck); // Do the ping (processing continues, interrupt will call echoCheck to look for echo).
counter++;
if (counter == SONAR_NUM) {
  Timer1.setPeriod(PAUSE_FOR_INTERFERENCE); // Sensor ping cycle complete, do something with the results.
  counter = 0;
}
}



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

Great news - Thx for confirning
(This seems like a fun Project)

Relying on pure internal clocks for not sending ranging pings at the same time is probably not super stable and if that happens then your robots are in a non recoverable state. Given you have communication between them - you could consider having a token based negotiation algorithm where a robot can start its ping sequence only if he got the token

Have fun!