Go Down

Topic: Manage multiple HC SR04 sensors using Arduino UNO's Timer1 to build a radar (Read 2473 times) previous topic - next topic

Starscream

Hi all,

I'm new in this forum... I bought an Arduino UNO and 6 HC SR04 sensors a few days ago.
I've googled a bit and I found the NewPing library

which lets you manage the sensors' ping.  :)
I've used the example shown here

and I think it is great if you're playing with the Arduino, but I want something more from this great board, than it.


I'd like to build a Event-driven system using the Timer1 which is into UNO's chip.
I've thought to use the internal timer to manage the Task which get the data from the sensors using the Job "read_distance".
So every period X, the timer is pinging the sensors one-by-one to get the different distances from them.
I've written some lines of code and if I use only one sensor the program works perfectly, but when I increase the number of sensors it doesn't work anymore.
Here you are my code:
Code: [Select]

 #define trigPin  2   // Trigger Pin
 
 #define echoPin1 5    // Echo Pin for sensor # 1
 #define echoPin2 6    // Echo Pin for sensor # 2
 #define echoPin3 7    // Echo Pin for sensor # 3
 #define echoPin4 8    // Echo Pin for sensor # 4
 #define echoPin5 9    // Echo Pin for sensor # 5
 #define echoPin6 10   // Echo Pin for sensor # 6
 
 #define SENSORS_NUMBER 6
 #define MAXIMUM_RANGE  200  // Maximum range needed
 #define MINIMUM_RANGE  0    // Minimum range needed
 
 volatile boolean time_elapsed;
 volatile byte i;
 
 volatile byte vect[] = {-2, -2, -2, -2, -2, -2};
 
 void read_distance(byte sensor_trig_pin, byte sensor_echo_pin, byte sensor_id);
 
 void setup()
 {
     pinMode(trigPin, OUTPUT);
     
     pinMode(echoPin1, INPUT);
     pinMode(echoPin2, INPUT);
     pinMode(echoPin3, INPUT);
     pinMode(echoPin4, INPUT);
     pinMode(echoPin5, INPUT);
     pinMode(echoPin6, INPUT);
     
     // open the serial port at 9600 bps:
     Serial.begin(9600);
 
     // initialize Timer1
     cli();          // disable global interrupts
     
     TCCR1A = 0;     // set entire TCCR1A register to 0
     TCCR1B = 0;     // set entire TCCR1B register to 0
 
     // set compare match register to desired timer count, corresponds to 1 second
     OCR1A = 15624;   // Timer 1 Output Compare Register A
     
     // turn on CTC mode on Timer1:
     TCCR1B |= (1 << WGM12);
     
     // Set CS10 and CS12 bits for 1024 prescaler:
     TCCR1B |= (1 << CS10);
     TCCR1B |= (1 << CS12);
     
     // enable timer compare interrupt:
     TIMSK1 |= (1 << OCIE1A);
     
     // enable Timer1 overflow interrupt:
     //TIMSK1 = (1 << TOIE1);
     
     // enable global interrupts:
     sei();    
   
     time_elapsed = false;  
 }
 
 ISR(TIMER1_COMPA_vect)
 {
   read_distance(trigPin, echoPin1, 0);
   read_distance(trigPin, echoPin2, 1);
   read_distance(trigPin, echoPin3, 2);
   read_distance(trigPin, echoPin4, 3);
   read_distance(trigPin, echoPin5, 4);
   read_distance(trigPin, echoPin6, 5);
   
   time_elapsed = true;  
 }
 
 void loop()
 {    
   if(time_elapsed){
     
     for(i = 0; i < SENSORS_NUMBER; i++){
        Serial.print(" # ");
        Serial.print(i+1);
        Serial.print(" :");
        Serial.println(vect[i]);
     }
     time_elapsed = false;  
   }
 }
 
 void read_distance(byte sensor_trig_pin, byte sensor_echo_pin, byte sensor_id){
   
   long duration; // Duration used to calculate distance
   long distance;
   
   digitalWrite(sensor_trig_pin, LOW);
   delayMicroseconds(2);
   
   digitalWrite(sensor_trig_pin, HIGH);
   delayMicroseconds(10);
   
   digitalWrite(sensor_trig_pin, LOW);
   duration = pulseIn(sensor_echo_pin, HIGH);
   
   //Calculate the distance (in cm) based on the speed of sound.
   distance = duration/58.2;
   
   if (!(distance >= MAXIMUM_RANGE || distance <= MINIMUM_RANGE)){
     vect[sensor_id] = distance;
   }
   else{
     vect[sensor_id] = -1;
   }
 }


I've configured Timer1 with CTC mode and I've set the prescale to 1024 (# Divisions).
The OCR1A value has been evaluated according to the following formula:



The value of OCR1A corresponds to 1 second; so every time Timer1 reaches that value, 1 second has gone, ISR function is executed, and inside of it there are 6 calls to the function which get distances using the sensors.
All the sensors uses the same trigPin, the only pin which changes is the echoPin.

I can't understand where is the problem, maybe the timer but I'm not shure.
Can you help me please?

You can see my elcetrical scheme here attached to the topic.



AWOL

Code: [Select]
ISR(TIMER1_COMPA_vect)
  {
    read_distance(trigPin, echoPin1, 0);
    read_distance(trigPin, echoPin2, 1);
    read_distance(trigPin, echoPin3, 2);
    read_distance(trigPin, echoPin4, 3);
    read_distance(trigPin, echoPin5, 4);
    read_distance(trigPin, echoPin6, 5);
   
    time_elapsed = true; 
  }

Bad idea.

Why are you doing all that time-consuming stuff in an ISR?
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Starscream

How could I avoid it? I think it is necessary if I want to get the distances from my sensors...
Can you suggest me a better idea, please?  :)

AWOL

Why is it necessary to use an interrupt?
Have a look at the blink without delay example.
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Starscream

I've seen it, but I want to use Timer's interrupt because in the main loop I can do something else with less priority than the Sensors_Ping task.
I think using interrupts is more efficient than the other ways...

AWOL

Quote
I think using interrupts is more efficient than the other ways

But if it doesn't work, perhaps a more pragmatic approach is required.
Your call.
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

wildbill

It looks like you're triggering all the sensors at the same time. Unless they are far apart, they're likely to interfere with each other. Even if you trigger them one at a time, you're likely to need a delay for returns from further objects to die away. Delaying in an interrupt routine is not recommended.

AWOL

It hardly seems necessary to point out that the speed of sound provides a very real delay.
But I'll do it anyway.
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Starscream

I want to do something like this:



they could interfere but I'm not sure.
Changing my code, I reduced Timer1's period from 1 second to 0.1 second and I ping the sensors one at period. It works, but I've divided the tak with six jobs, into 6 tasks with one job per task.
In this way the tasks will run more frequently than what it did the "big" old task.

Here you are the changes:

I've changed OCR1A's value, it corresponds to 0.1 seconds:
Code: [Select]

      // set compare match register to desired timer count:
      OCR1A = 1561;   // Timer 1 Output Compare Register A


I've introduced a new global variable:
Code: [Select]

volatile byte current_sensor;
void setup()
  {
      ...
      current_sensor = 0;
  }


I've changed the ISR function:
Code: [Select]

ISR(TIMER1_COMPA_vect)
  {
    switch(current_sensor){
      case 0:  read_distance(trigPin, echoPin1, 0);
               current_sensor = 1;
        break;
      case 1:  read_distance(trigPin, echoPin2, 1);
               current_sensor = 2;
        break;
      case 2:  read_distance(trigPin, echoPin3, 2);
               current_sensor = 3;
        break;
      case 3:  read_distance(trigPin, echoPin4, 3);
               current_sensor = 4;
        break;
      case 4:  read_distance(trigPin, echoPin5, 4);
               current_sensor = 5;
        break;
      case 5:  read_distance(trigPin, echoPin6, 5);
               current_sensor = 0;
        break;
    }
   
    time_elapsed = true; 
  }


I've changed the loop in this way:
Code: [Select]

void loop()
  {   
    if(time_elapsed){
     
      Serial.print(" # ");
      Serial.print(current_sensor+1);
      Serial.print(" :");
      Serial.println(vect[current_sensor]);
     
      time_elapsed = false; 
    }
  }


This is not what I've thought, but it works!
If you have any better idea tell me please!

AWOL

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Starscream

Yes, I have. But I don't think it will be as accurate as Timer1's interrupt.
It simply checks, periodicaly, if a value is bigger than an other, so it isn't a "real-time" criteria...

AWOL

Quote
But I don't think it will be as accurate as Timer1's interrupt.

I'm sorry, I really don't see that it matters.
Is there some hard real-time requirement for range data?
Does it really matter if there is +/- 1ms jitter on the times these measurements are taken?
(bearing in mind that 1ms is about 34cm)
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Starscream

I think you're right.
I'm developing this project because I'd like to build a little drone. That's why I have placed the sensors in this position (see the schema above).
The drone will receive instruction from a PC via wirless communication and I don't it to crash against an obstacle will it 's receiving the instruction set.

PaulS

Quote
The drone will receive instruction from a PC via wirless communication and I don't it to crash against an obstacle will it 's receiving the instruction set.

I think you need to do three things:
1) Look at the examples that come with the NewPing library. It already provides for scheduled reading from the sensors. Let the library handle when to read again.

2) Stop replicating code and pretending to have an array of sensors and pins. Use real arrays.

3) Concentrate on reading the PC data as fast as possible, and quit worrying about stuff that is already interrupt driven. Worry about the stuff that really will take time.

Go Up