Distance between arduinos

Hello,
I am tying to communicate some arduinos through a Time Difference of Arrival DtoA.

The proyect consist in an indoor positioning system with three beacons and one receiver.

I use a NRF24l01 module for de radiofrecuency with the library RF24master and an ultrasonic device wich signal is detected through de analog comparator of the arduino with the library analogComp.

I have a code that works pretty well, just to measure the distance between one beacon and teh receiver, but when i try to introduce more staff, like the sound speed as a function of temperature the distance measure is awfull.

I have allready checked all the conexions of the arduinos.

The first code i used for the beacon is:

#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
#include <SPI.h>

char msg[16]= "Baliza 1 0 0 1";
int CR=0;
int CU=0;
int i;

RF24 radio(9,10);
const uint64_t pipes[2] = {0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL};

void startRadio()
{
  radio.stopListening();
  radio.write(msg, 16);
  //Serial.println("r");
  CR=1;
}
void Beacon()
{
  noInterrupts();
  for (i=0; i<=8; i++)
  {    
    PORTD = PORTD & B11011111 | B00010000;
    delayMicroseconds(12.5);
    PORTD = PORTD & B11101111 | B00100000;
    //PORTD |= B00001000;
    delayMicroseconds(12.5);
  }
  interrupts();
  //Serial.println("u");
  CU=1;
}

void setup() 
{
  Serial.begin(9600);
  DDRD= DDRD | B00110000;
  radio.begin();
  radio.openWritingPipe(pipes[1]);
  radio.openReadingPipe(1,pipes[0]);
  radio.setRetries(15,15);
}

void loop() 
{
    startRadio();
    Beacon();
   // delay(500);
}

And the code for the receiver is:

#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
#include <SPI.h>
#include <TimerOne.h>
#include <analogComp.h>

char msgint[16]="Baliza 1";
char msg[16];
int CR=0, CU=0;
int t1=0;
boolean s;
boolean r=0;

RF24 radio(9,10);
const uint64_t pipes[2] = {0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL};

int ER()
{
  if(r == 0)
  {
    radio.startListening();
    r=1;
  }
  if (radio.available())
  {
    int done = radio.read(msg, 16);
    if (compmsgs(msgint, msg)==0)
    {
      Timer1.start();
      CR=1;
    }
  }
  if(compmsgs(msgint, msg)==0 && CR == 1 && r == 1)
  {
    radio.stopListening();
    r=0;
  }
}

int EU ()
{
  analogComparator.setOn(AIN0, AIN1); //we instruct the lib to use voltages on the pins
  s= false;
  analogComparator.enableInterrupt(changeStatus, RISING); //we set the interrupt and when it has to be raised
  if (s == true)
  {
    t1=Timer1.read();
    //Timer1.stop();
    CU=CU+1;
  }
  if(s == true && CU == 1)
  {
    analogComparator.disableInterrupt(); 
    analogComparator.setOff(); 
  }
}

void changeStatus()
{
  s= true;
}

unsigned char compmsgs(char *code, char *codeintro)
{
  while(*code!=0 && *codeintro!=0)
  {
    if(*code!=*codeintro)
    {
      return 1;
    }
    code++;
    codeintro++;
  }
  return 0;
}

void setup(){
  Serial.begin(9600);
  radio.begin();
  radio.openWritingPipe(pipes[0]);
  radio.openReadingPipe(1,pipes[1]);
  Timer1.initialize();
}
 
void loop()
{
  if (CR == 0)
  {
    ER ();
  }
  if (CU == 0 && CR == 1)
  {
    EU ();
  }
  if (CR == 1 && CU == 1)
  {
    float distancia = t1*0.034;
    Serial.print(t1);
    Serial.print("  ");
    Serial.println(distancia);
    CR=0;
    CU=0;
  }
}

and the code that works awfull is:

#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
#include <SPI.h>
#include <TimerOne.h>
#include <analogComp.h>

boolean s;
boolean r=0;
char msgint[16]="Baliza 1 0 0 1";
char msgtostring[16];
String msg;
int CR=0, CU=0;
int t1=0;
int done;
float T=0;
float tiempofallo;

RF24 radio(9,10);
const uint64_t pipes[2] = {0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL};

int ER()
{
  if(r == 0)
  {
    radio.startListening();
    r=1;
  }
  if (radio.available())
  {
    done = radio.read(msgtostring, 16);
    if (/*compmsgs(msgint, msg)==0*/ done == 1)
    {
      Timer1.restart();
      CR=1;
      Serial.println("r");
    }
  }
  if(/*compmsgs(msgint, msg)==0*/ done == 1 && CR == 1 && r == 1)
  {
    radio.stopListening();
    r=0;
  }
}

int EU ()
{
  analogComparator.setOn(AIN0, AIN1); //we instruct the lib to use voltages on the pins
  //s= false;
  analogComparator.enableInterrupt(changeStatus, RISING); //we set the interrupt and when it has to be raised
  if (s == true)
  {
    t1=Timer1.read();
    //Timer1.stop();
    CU=1;
      Serial.println("u");
  }
  if(s == true && CU == 1)
  {
    analogComparator.disableInterrupt(); 
    analogComparator.setOff(); 
    Serial.println(s);
    s= false;
  }
}

void changeStatus()
{
  s= true;
}

unsigned char compmsgs(char *code, char *codeintro)
{
  while(*code!=0 && *codeintro!=0)
  {
    if(*code!=*codeintro)
    {
      return 1;
    }
    code++;
    codeintro++;
  }
  return 0;
}

unsigned long ftemperatura()
{
  int T10=analogRead(A0);
  float T11=analogRead(A0)*(5000.0 / 1024.0);
  float T12=T11/10;
  return T12;
}

void setup(){
  Serial.begin(9600);
  radio.begin();
  radio.openWritingPipe(pipes[0]);
  radio.openReadingPipe(1,pipes[1]);
  Timer1.initialize(1000000);
  T=ftemperatura();
  tiempofallo=(2000/(331.4+0.6*T));
}
 
void loop()
{
  if(t1 < tiempofallo)
  {
    if(CR==0 && CU==0)
    ER ();
    if(CR==1 && CU==0)
    EU ();
    if (CR == 1 && CU == 1)
    {
      float distancia = t1*((331.4+0.61*T)/1000);
      Serial.println(distancia);
      Serial.print(CR);
      Serial.print(CU);
      CR=0;
      CU=0;
      done=0;
      Serial.print(CR);
      Serial.println(CU);
    }
  }
  /*else
  {
    Serial.println("fuera de rango");
    Timer1.restart();
    CR=0;
    CU=0;
  }*/
}

I am desperate because im really stacked with this code, if someone have an idea please help me.

I have a code that works pretty well, just to measure the distance between one beacon and teh receiver, but when i try to introduce more staff, like the sound speed as a function of temperature the distance measure is awfull.

You said these beacons are indoors. Just how much does the indoor temperature vary? Just how much change in speed does that variation cause?

PaulS:
You said these beacons are indoors. Just how much does the indoor temperature vary? Just how much change in speed does that variation cause?

The temperature doesnt vary usually, the speed of sound doesnt change so much.
Why?

I have tried to look at your working and non-working Receiver programs side by side but I can't figure out the rationale for any of it. Meaningless names like EU and CR don't help.

Can you provide a "walk-through" description of how the working code works - i.e. what each part does. And then do the same for the modified version.

As you have not posted a second version I am assuming there is no change to the TX code.

What version of the RF24 library are you using?

...R
Simple nRF24L01+ Tutorial

malacay:
The temperature doesnt vary usually, the speed of sound doesnt change so much.
Why?

I think the question should be: why bother?

Air pressure also changes the speed of sound. Higher pressure means faster sound.

Such effects only become relevant if the error is significant. If the speed of sound changes by maybe 0.01% over your common temperature range, but your error due to other causes is 1%, it's a total waste of effort.

If the distance calculation in the working receiver code is correct float distancia = t1*0.034;
then the formula in the non-working receiver looks odd

float distancia = t1*((331.4+0.61*T)/1000);

For any positive value of "T", the multiplier in the second example is at least 10x greater than the first example.

I'd check the source of that equation. Moreover, I would print the values going into the formula "t1" and "T" to make sure the measurements look plausible.

wvmarle:
Air pressure also changes the speed of sound. Higher pressure means faster sound.

No it doesn't. Counter-intuitive for sure. :slight_smile: It's only a function of temperature, once you assume the composition of the air is constant.

The formula with temperature looks like the formula in Speed of sound - Wikipedia.

Robin2:
I have tried to look at your working and non-working Receiver programs side by side but I can't figure out the rationale for any of it. Meaningless names like EU and CR don't help.

Can you provide a "walk-through" description of how the working code works - i.e. what each part does. And then do the same for the modified version.

As you have not posted a second version I am assuming there is no change to the TX code.

What version of the RF24 library are you using?

...R
Simple nRF24L01+ Tutorial

You are right i am going to post a walk-through description.

I can´t determinate the version of the library but i have download here, fist for the nrf24l01, second for the analog comparator:

The code should work in the next way:
First, the beacon sends a RF signal and an ultrasound signal.
Then, the receiver gets the RF signal and start a conunter.
After that, when the ultrasound is heared gets the time of the counter.
Finally, the maths to get the distance are solved, and i get that distance,

#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
#include <SPI.h>

char msg[16]= "Baliza 1 0 0 1";
int CR=0;
int CU=0;
int i;

RF24 radio(9,10);
const uint64_t pipes[2] = {0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL};

void startRadio()  // here i start the radio module and send the message
{
  radio.stopListening();
  radio.write(msg, 16);
  //Serial.println("r");
  CR=1;
}
void Beacon() //here the ultrasound is sended
{
  noInterrupts();
  for (i=0; i<=8; i++)
  {    
    PORTD = PORTD & B11011111 | B00010000;
    delayMicroseconds(12.5);
    PORTD = PORTD & B11101111 | B00100000;
    //PORTD |= B00001000;
    delayMicroseconds(12.5);
  }
  interrupts();
  //Serial.println("u");
  CU=1;
}

void setup() 
{
  Serial.begin(9600);
  DDRD= DDRD | B00110000;
  radio.begin();
  radio.openWritingPipe(pipes[1]);
  radio.openReadingPipe(1,pipes[0]);
  radio.setRetries(15,15);
}

void loop() 
{
    startRadio();
    Beacon();
   // delay(500);
}

for the reciver codes, first the one that works well:

#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
#include <SPI.h>
#include <TimerOne.h>
#include <analogComp.h>

char msgint[16]="Baliza 1";
char msg[16];
int CR=0, CU=0;
int t1=0;
boolean s;
boolean r=0;

RF24 radio(9,10);
const uint64_t pipes[2] = {0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL};

int ER() // function for the receving rf message 
{
  if(r == 0)
  {
    radio.startListening(); // the radio start listening, i don´t know why but if this function is out that if() 
    r=1;                          // it doesn´t works correctly
  }
  if (radio.available())
  {
    int done = radio.read(msg, 16);
    if (compmsgs(msgint, msg)==0)
    {
      Timer1.start();       // if the message is correct start a counter
      CR=1;
    }
  }
  if(compmsgs(msgint, msg)==0 && CR == 1 && r == 1)
  {
    radio.stopListening();
    r=0;
  }
}

int EU ()  // the internal analog comparator of the arduino is enabled
{
  analogComparator.setOn(AIN0, AIN1); //we instruct the lib to use voltages on the pins
  s= false;
  analogComparator.enableInterrupt(changeStatus, RISING); //we set the interrupt and when it has to be raised
  if (s == true)
  {
    t1=Timer1.read();        // when the pin in the arduino gets a read a high, i read the time of the timer.
    //Timer1.stop();
    CU=CU+1;
  }
  if(s == true && CU == 1)
  {
    analogComparator.disableInterrupt(); 
    analogComparator.setOff(); 
  }
}

void changeStatus()
{
  s= true;
}

unsigned char compmsgs(char *code, char *codeintro)
{
  while(*code!=0 && *codeintro!=0)
  {
    if(*code!=*codeintro)
    {
      return 1;
    }
    code++;
    codeintro++;
  }
  return 0;
}

void setup(){
  Serial.begin(9600);
  radio.begin();
  radio.openWritingPipe(pipes[0]);
  radio.openReadingPipe(1,pipes[1]);
  Timer1.initialize();
}
 
void loop()
{
  if (CR == 0)  // here i have the other variables "CR" and "CU" just to ensure the program is running 
  {                  // correctly
    ER ();
  }
  if (CU == 0 && CR == 1)
  {
    EU ();
  }
  if (CR == 1 && CU == 1)
  {
    float distancia = t1*0.034; // in this program i get the measure in milimeters
    Serial.print(t1);
    Serial.print("  ");
    Serial.println(distancia);
    CR=0;
    CU=0;
  }
}

and in the other code:

#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
#include <SPI.h>
#include <TimerOne.h>
#include <analogComp.h>

boolean s;
boolean r=0;
char msgint[16]="Baliza 1 0 0 1";
char msgtostring[16];
String msg;
int CR=0, CU=0;
int t1=0;
int done;
float T=0;
float tiempofallo;

RF24 radio(9,10);
const uint64_t pipes[2] = {0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL};

int ER()
{
  if(r == 0)
  {
    radio.startListening();
    r=1;
  }
  if (radio.available())
  {
    done = radio.read(msgtostring, 16);  //it has the same function in this program than in the other
    if (compmsgs(msgint, msg)==0)
    {
      Timer1.restart();
      CR=1;
      Serial.println("r");
    }
  }
  if(compmsgs(msgint, msg)==0 && CR == 1 && r == 1)
  {
    radio.stopListening();
    r=0;
  }
}

int EU () // and in this function should do the same 
{
  analogComparator.setOn(AIN0, AIN1); //we instruct the lib to use voltages on the pins
  //s= false;
  analogComparator.enableInterrupt(changeStatus, RISING); //we set the interrupt and when it has to be raised
  if (s == true)
  {
    t1=Timer1.read();
    //Timer1.stop();
    CU=1;
      Serial.println("u");
  }
  if(s == true && CU == 1)
  {
    analogComparator.disableInterrupt(); 
    analogComparator.setOff(); 
    Serial.println(s);
    s= false;
  }
}

void changeStatus()
{
  s= true;
}

unsigned char compmsgs(char *code, char *codeintro)
{
  while(*code!=0 && *codeintro!=0)
  {
    if(*code!=*codeintro)
    {
      return 1;
    }
    code++;
    codeintro++;
  }
  return 0;
}

unsigned long ftemperatura() // here i just read the temperature with a LM35
{
  int T10=analogRead(A0);
  float T11=analogRead(A0)*(5000.0 / 1024.0);
  float T12=T11/10;
  return T12;
}

void setup(){
  Serial.begin(9600);
  radio.begin();
  radio.openWritingPipe(pipes[0]);
  radio.openReadingPipe(1,pipes[1]);
  Timer1.initialize(1000000);
  T=ftemperatura();
  tiempofallo=(2000/(331.4+0.6*T)); // the maximun time for waiting an ultrasound 
}
 
void loop()
{
  if(t1 < tiempofallo)
  {
    if(CR==0 && CU==0)
    ER ();
    if(CR==1 && CU==0)
    EU ();
    if (CR == 1 && CU == 1)
    {
      float distancia = t1*((331.4+0.61*T)/1000); // here i get the measure in centimeters
      Serial.println(distancia);
      Serial.print(CR);
      Serial.print(CU);
      CR=0;
      CU=0;
      done=0;
      Serial.print(CR);
      Serial.println(CU);
    }
  }
  else
  {
    Serial.println("fuera de rango"); //if it is out of range sets the counter to 0
    Timer1.restart();
    CR=0;
    CU=0;
  }
}

I suggest you delete the ManiacBug version of the RF24 library and use the newer TMRh20 version which solves some problems. There is a link to it in my Tutorial.

Your TX program makes no attempt to determine if the wireless message has been received. To my mind it should only start the ultrasound when it receives an acknowledgement from the RX. Without something like that how can the two systems know that the noise corresponds to a particular wireless signal?

And you have not described what is different about the non-working version of the Rx program.

...R

malacay:
First, the beacon sends a RF signal and an ultrasound signal.
Then, the receiver gets the RF signal and start a conunter.

This part is technically sound - BUT the RF24 is not a beacon, it's a communication device. It uses some protocol to make a connection, and send a message, and that by nature makes wireless not time reliable. Only if you can make sure somehow that this error is no more than say 10 µs (about 3 cm in distance) you can use this to time how much longer it takes fro the sound to arrive and have a reasonably accurate measurement.

Wirelessly measuring the distance between two devices accurately is not easy at all.

wvmarle:
Only if you can make sure somehow that this error is no more than say 10 µs (about 3 cm in distance) you can use this to time how much longer it takes fro the sound to arrive and have a reasonably accurate measurement.

A round-trip nRF24 message (message plus acknowledgement) takes a few millisecs. But I have never had any reason to check how consistent the round-trip time is.

However it could vary significantly if there was wireless interference causing it to make a number of retries before it was successful. That's why I thought that basing the timing off the receipt of the acknowledgement might be better. However I had not done the maths and had not realized that 10 µs accuracy would be needed.

A simple light beacon might be a more reliable means to start the timing.

...R

You still have the problem of ambient light interference, so you need to use modulation, like IR remote controls.

When using IR a typical modulation frequency is 38 kHz, that's a 26 µs period. The problem is now: after how many pulses will the modulation be recognised as having started, and how can you be sure that you didn't miss the first one? That'd kill your accuracy there and then. Maybe have the transmitter send a number of pulses of defined length so the receiver knows the message is coming, then one with a different length (say double the length), and at the end of that long pulse start the sound pulse?

Both LEDs and phototransistors should be fast enough for this part.

I did a similar project with two nrf24 and two hc-sr04. Got good (consistently good) distance-btwn-units with a small correction for latency. Timing started on receipt of radio message.

wvmarle:
You still have the problem of ambient light interference,

I was thinking of a bright flash that would be detectable in the ambient light.

...R

DaveEvans:
I did a similar project with two nrf24 and two hc-sr04. Got good (consistently good) distance-btwn-units with a small correction for latency. Timing started on receipt of radio message.

Interesting.

Can you post the details?

...R

Not now - traveling til end of month.

In the thread linked below, I posted code to do this sort of thing using basic 433 MHz modules. As noted above, some of the protocol and retry features of more sophisticated data links would seem to be disadvantageous in this application.

http://forum.arduino.cc/index.php?topic=451490.msg3131070#msg3131070

Robin2:
I suggest you delete the ManiacBug version of the RF24 library and use the newer TMRh20 version which solves some problems. There is a link to it in my Tutorial.

Your TX program makes no attempt to determine if the wireless message has been received. To my mind it should only start the ultrasound when it receives an acknowledgement from the RX. Without something like that how can the two systems know that the noise corresponds to a particular wireless signal?

And you have not described what is different about the non-working version of the Rx program.

...R

The difference is the problem because in the non-working program, I introduce a LM35 to take the temperature and when the code for the LM35 and the equation is changed to introduce the speed of sound dependent of temperature, the system stop working and provides erroneal measures. I am going to try with the TMRh20 library.

You need to re-organize your program with meaningful variable names. I mentioned this earlier.

For example you have this line

 T=ftemperatura();

It is quite impossible to identify all the lines of code that use the variable called T because so many words have the letter T in them.

...R

Thank all of you for using your time helping ir understandig what i want to do,
I am posting because i have my problem solved, i don´t really know how this happen, but i think its because one of the pins of NRF24l01 was not making contact with the conector even i tested with a multimeter, the fact is when i moved a little the system start working.

I want to say that very grateful with your help.
Thank you all.