speed measurement with 2 arduino boards wireless connected

Hi,

I have 2 UNO boards, eacht board has a sensor and a HC12 module. I want the first board to detect an object with the sensor and send the nummer of millis that are passed since startup to the second board. The second board also detects the object passing by and calculates the millis from the fist board with its own millis.

Ofcourse there must be an initialising phase where the delta in millis since startup between the to boards are calculated. So during this phase board 1 sends a couple of times its millis and the second boards captures this and calculates the delta millis. So the speed calculation must consider this delta.

The software on the boards are up and running and I am trying to set up the init phase, but I see that the delta keeps changing. For every 1000ms the calculated delta increases with 2 ms.

Is this a wrong approach?

without seeing the code, who knows?

The problem is the length of time it takes for a wireless message to be sent and received. There is no guarantee that it will take any specific amount of time - for example a message might be garbled and need to be re-sent.

If the sending Arduino records the value of millis() when something happens and later sends that number together with its latest value of millis() AND IF the message can be assumed to have arrived as soon as possible the receiving Arduino could work out how long ago (relative to its own values for millis() ) the event was detected.

For example suppose the event was timed by the first Arduino at 56007 millis() and the message was sent at 69334 millis() and was received by the second Arduino at 8347 millis() (according to its clock) and the second event happened at 9227 millis() it would be clear that according to the clock of the receiving Arduino the first event happened at 8347 - (69334 - 56007) = -4980 millis() so the elapsed time would be 9227 - (-4980) = 14207 millisecs.

However if it takes an appreciable amount of time for the message to arrive then there will be an error in the calculation. And "appreciable amount of time" depends very much on how accurate the time measurement needs to be.

It would be instructive to set up a test in which the message is sent both by wire and by wireless. However the wireless evidence will only be valid for that occasion at that location.

I have not used a HC12 (only nRF24L01+ modules) so I don't know if it has an automatic acknowledgement system that would allow the sender to know is a message was received. If it has then that could be used to help with the reliability by allowing a failed message to be resent with an updated value of the sender's current value of millis()

...R

I have not used a HC12 (only nRF24L01+ modules) so I don't know if it has an automatic acknowledgement system that would allow the sender to know is a message was received.

just plain old fashioned half duplexSerial data, no ack, no checksum.

I would have transmitter1 send messages on the second and transmitter2 send messages on the half second. Transmitter2 would save the millisecond it received the message from transmitter1 and send that data back along with any measurements.

Transmitter1 would then be able to calculate the round trip delay time and you could assume that the one way delay is half that. If the transmitters are motionless and calculation of the delay is consistent over many communication loops, you would have a reasonable guess of the clock offset of the timer in transmitter2.

Your accuracy would be as small as the variation in the loop delay times.

edmcguirk:
you would have a reasonable guess of the clock offset of the timer in transmitter2.

I reckon if the sender includes its own millis() value at the time of sending there is no need to figure out the clock offset.

However it would be important to do tests to figure out if the length of time between sending and receiving is short enough not to matter.

If, as @Geek Emeritus has said, the data is sent in Serial format the transmission rate is likely to be slow and it would perhaps be essential to adjust the time calculations to allow for the baud-rate and the number bytes of data.

And it seems to me that a pair of nRF24L01+ modules might be more suitable if the two Arduinos are within their range.

...R

When the first radio is triggered, just send an empty byte. Have the second radio polling the data received bit in a tight loop, when the received bit changes state, start the timer, when the second radio is triggered by the passing target, stop the timer and and do the stop - start math.

Robin2:
I reckon if the sender includes its own millis() value at the time of sending there is no need to figure out the clock offset.

If you know the other transmitter sends data only on the half second, you already know the other transmitter's timestamp.

edmcguirk:
If you know the other transmitter sends data only on the half second, you already know the other transmitter's timestamp.

Only if a message is never dropped.

Or what happens if the sender is reset without going through a new SYNC routine?

...R

Robin2:
Only if a message is never dropped.

Or what happens if the sender is reset without going through a new SYNC routine?

...R

Well,if the message is dropped, the data is lost anyway. My suggestion implies that all communication starts with a message from transmitter1 and ends with a reply from transmitter2 that includes the sent timestamp from transmitter1 and transmitter2 synchronizes its transmission window to its reception from transmitter1.

If there is a any reset, the whole sync process needs to start over.

I like sending on clock pulses over sending arbitrarily with a timestamp only because it also helps avoid collisions if all transmitters are time coordinated. Provided you have idle time and clean environment.

You could just as easily always send the external timestamp of the last received message with the internal timestamp of the current sent message during the same anti-collision windows for the cost of a little more data in the package.

You could save even more data in the package if you send your reply exactly 500 milliseconds after receiving a message with your timestamp referenced to when you received the first message.

thanks for your reactions. I made the whole setup a lot simpeler for testing purposes.

To skip the wireless part I connected the TX/RX from the second Arduino to the first one.

With the first arduino I send every second the millis counter.
The second arduino receives this counter, compares this with its own counter and shows the delta.

Still the delta value increases with about 2 ms every second. What am I doing wrong here?

First arduino:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX
unsigned long counter = 0;

void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
}
mySerial.begin(9600);
Serial.println("Port init ok");

}

void loop()
{

counter = millis();
mySerial.print(counter); //send counter to the serial port
Serial.println(counter); // send countr to the serial monitor
delay(1000);
}

Second arduino:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX
unsigned long counter = 0;
unsigned long counter2 = 0;

void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) { }

mySerial.begin(9600);
Serial.println("Port init ok");
}

void loop() { // run over and over

if (mySerial.available()) {
counter = mySerial.parseInt();
if (counter == 0) return;
counter2 = millis();
Serial.print("Counter : ");Serial.println(counter);
Serial.print("Counter 2: ");Serial.println(counter2);
Serial.print("Delta : ");Serial.println(counter2 - counter);
}
}

You can't use Serial.parseInt() for this sort of thing because it is a blocking function and could itself be responsible for the problem.

Have a look at the examples in Serial Input Basics - simple reliable non-blocking ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.

The technique in the 3rd example will be the most reliable. It is what I use for Arduino to Arduino and Arduino to PC communication.

You can send data in a compatible format with code like this (or the equivalent in any other programming language)

Serial.print('<'); // start marker
Serial.print(value1);
Serial.print(','); // comma separator
Serial.print(value2);
Serial.println('>'); // end marker

Also, use the highest baud rate that you could use with the HC12.

HOWEVER ...
I don't think this matters

Still the delta value increases with about 2 ms every second.

All that is important is whether the elapsed time (between something being detected by the first Arduino and the other thing being detected by the second Arduino) when calculated by the second Arduino is the same as when you measure it with a stop watch, or with a single Arduino detecting both events.

...R

does the parseint function really blocks the millis of micros from counting on?? Thas sounds crazy, but I will ofcourse try the code from the example and will post the results here.

I do beleive that getting a steady delta value is important. All I want is to know the delta in millis passed from both boards. How else could I have a stopwatch function with two arduino boards communicating via the air?
the latency between transmiiting and sending can not affect the stopwatch function. So with a good delta I can caluculate the passing time, even when there was a delay in the trasmission.

I will start changing the code and post the result.

I changed the code, but there is no difference. Here you can see that the delta increases.
Communication through connecting TX/RX directly. The code is at hte bottom of this post.

Counter : 10062 (millis sent from arduino one to arduino 2)
Counter 2: 13787 (millis from arduino 2)
Delta : 3725

Counter : 11071
Counter 2: 14796
Delta : 3725

Counter : 12079
Counter 2: 15806
Delta : 3727

Counter : 13086
Counter 2: 16817
Delta : 3731

Counter : 14094
Counter 2: 17826
Delta : 3732

Counter : 15101
Counter 2: 18836
Delta : 3735

Counter : 16110
Counter 2: 19847
Delta : 3737

Counter : 17118
Counter 2: 20856
Delta : 3738

Counter : 18125
Counter 2: 21866
Delta : 3741

Counter : 19133
Counter 2: 22876
Delta : 3743

Counter : 20142
Counter 2: 23886
Delta : 3744

Counter : 21149
Counter 2: 24896
Delta : 3747

Counter : 22157
Counter 2: 25906
Delta : 3749

Code Arduino one:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX
unsigned long counter = 0;

void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
}
mySerial.begin(9600);
Serial.println("Port init ok");

}

void loop()
{
counter = millis();
mySerial.print('<');
mySerial.print(counter); //send counter to the serial port
mySerial.print('>');

Serial.print('<');
Serial.print(counter); // send countr to the serial monitor
Serial.println('>');
delay(1000);
}

Code Arduino two

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX
unsigned long counter = 0;
unsigned long counter2 = 0;
const byte numChars = 32;
char receivedChars[numChars];
boolean newData = false;

void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) { }

mySerial.begin(9600);
Serial.println("Port init ok");
}

void loop()
{ // run over and over
recvWithStartEndMarkers();
showNewData();
}

void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;

while (mySerial.available() > 0 && newData == false) {
rc = mySerial.read();

if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}

else if (rc == startMarker) {
recvInProgress = true;
}
}
}

void showNewData() {
if (newData == true) {
//Serial.print("This just in ... ");
counter = atoi(receivedChars);
counter2 = millis();
Serial.print("Counter : ");Serial.println(counter);
Serial.print("Counter 2: ");Serial.println(counter2);
Serial.print("Delta : ");Serial.println(counter2 - counter);
Serial.println("");

newData = false;
}
}

Did you swap the two Arduinos and check the delta?

Paul

Hi, yes I did. The delta is still increasing. So it must be something on arduino one that causes this problem. Perhaps the softwareserial lib? I will now try to use a mega so I do not need this lib

pbak1967:
The delta is still increasing.

Why does that matter?

...R

JCA79B:
When the first radio is triggered, just send an empty byte. Have the second radio polling the data received bit in a tight loop, when the received bit changes state, start the timer, when the second radio is triggered by the passing target, stop the timer and and do the stop - start math.

I believe this is the easiest solution, 2nd board does all the math. It doesn't really matter what the time is on the first board, once board 2 gets a signal, it simply starts timing and ends when it detects the object passing by.

Yes that would be a simple approach. But does not take into account latency's that might occur during the transmission.

Robin2:
Why does that matter?

...R

If you know that you have an error of 2 or 3 milliseconds per second but you know that your measurement is less than 100 milliseconds old, maybe it doesn't matter. But if it is a consistent error that is probably based on incorrect programming, I would want to fix it.

Since the delayed printout is supposed to be every second but each delayed printout is 10 milliseconds longer than one second, I think that could be a clue. The 'blink without delay' sketch examples are probably a better method for this project.

void setup() {
  nextTrigger = millis() + 1000;
}

void loop() {
  if (millis() >= nextTrigger) {

    // do stuff

    nextTrigger += 1000;
  } 
}