3

The point here is to syncronise a DS3231 module (on an Arduino) via serial port with NTP time by computer.

The problem here is that I'd like to know how much time is taken by the serial and I2C transmission, because if the PC sends the time at a time t1 and transmit it to the Arduino, the Arduino decodes it and sends it to the DS3231, the time t1 is received at a time t2 greater than t1.

PC (t1) => Serial(t1+x) => Arduino(t1+x+y) => I2C(t1+x+y+z) => DS3231(t2=t1+x+y+z)
  • t1 -> NTP time recorded by PC
  • x -> Time taken by serial transmission
  • y -> Time taken by arduino buffering/processing
  • z -> Time taken by I2C transmission at 100kHz
  • t2 -> Time when message is received by DS3231

Is it negligible for a precision at millisecond?

I found a table on Wikipedia that displays time per bit per baudrate. I am planning to use a baudrate of 57600.

I tested offset using a simple echo program available here at 300 baud:

String buff="";

void setup() {
  // put your setup code here, to run once:
Serial.begin(300);
}

void loop() {
  if(Serial.available())
  {
    while(Serial.available())
    {
      buff += char(Serial.read());
    }
    Serial.print(buff);
    buff = "";
  }
}
  • What Arduino are you using? – Majenko Jul 7 '18 at 10:24
  • I am using an arduino Uno or Nano with an ATMega238P – Storca Jul 7 '18 at 11:07
  • How is it "faster than expected"? What are you sending, and how are you measuring / calculating how fast it should be? – Majenko Jul 7 '18 at 11:19
  • 2
    How is the exact baud rate relevant to time synchronization? You have an NTP client, you are not going to use the serial port itself as a time source, are you? Please, clarify your question and clearly state what you are trying to achieve. – Edgar Bonet Jul 7 '18 at 13:22
  • 1
    @EdgarBonet I think the concern is that the PC has time X and transmits it at X. The Arduino finishes receiving that stated time at Y - the difference between X and Y being the time taken to do the transfer through serial. If you know Y-X then you can add (Y-X) to X to get the time when the transfer has completed. – Majenko Jul 7 '18 at 13:54
3

I second the general idea of Dmitry Grigoryev’s answer: your best option is to measure a round-trip time and use half of that as an estimate of the one-way time. Otherwise you will have too many unknowns. For example, you don't know how long your OS takes to process the outgoing message and send it through USB to the USB/serial bridge inside the Arduino. As an optimization, I would send the time information and the synchronization strobe as two separate messages, with the latter being a single byte. This way the latency of the synchronization strobe (the only latency that matters) is minimized.

Latency of serial communication

I did some research to try to nail this one down. At 57600 bits/s, the theoretical time per bit is about 17.4 µs, as stated in the Wikipedia article you linked to. In practice, the actual time can be rounded more or less depending on the UART capabilities and the clock speed. You did not specify what Arduino you are using. In the following, I will assume an AVR-based Arduino clocked at 16 MHz, which is presumably the most common.

The UART in the AVR constrains the bit time to be a multiple of 8 CPU cycles, i.e. 0.5 µs when running at 16 MHz. The closest you can get to the ideal timing is therefore 17.5 µs. However, the Arduino core library has a hard coded compatibility exception which, in this very specific case, sets the bit duration to 17 µs instead. Since you are presumably configuring the port at /8N1, which is the default, the time per byte is then 170 µs (1 start bit, 8 data bits and 1 stop bit).

Actually, when transmitting a single byte, the transmission time is slightly less than that. The UART can report the byte as received as soon as it is done taking the required three samples of the stop bit, which happens slightly after the middle of that stop bit. The theoretical time it takes to transmit a single byte is then about 9.56 times the single-bit duration, or about 162.56 µs at 57600/8N1.

I did a simple loopback test with the TX and the RX of my Uno shorted together. I used a timer to time the execution of the following code:

Serial.write('.');
while (!Serial.available()) continue;

The result was 182.38 µs, or about 10.73 bit durations. A fraction of this time is taken by the execution of Serial.write() and Serial.available(). In order to get closer to a bare hardware latency, I also timed a low-level version of the two lines above:

UDR0 = '.';
while (!bit_is_set(UCSR0A, RXC0)) continue;

This has to be run with interrupts disabled. The execution time was now between 164.19 and 164.81 µs, i.e. 9.66 to 9.69 bits durations.

Alternative approach

As a final note, I would recommend you consider buying a GPS module with a 1PPS output. This 1PPS is a 1 Hz logic signal with the rising edges tightly aligned to the start of the UTC seconds. You typically get an accuracy in the tens of nanoseconds. Since this is just a logic signal that you can easily compare with the 1 Hz output of your RTC module, you get rid of all the latency issues that come with computers (your NTP client), or data links (USB, serial, I2C).

  • Thank you for the answer :) ; gps utc time sync might also be a good option for precise time syncronisation – Storca Jul 11 '18 at 16:11
3

Transmission time will certainly not be negligible. To estimate it, you could use the roundtrip delay calculation, which is (roughly) how the NTP protocol is implemented in the first place.

  • at PC time T1, PC sends current time T1 to Arduino via UART
  • Arduino sets the time T1 in DS3231 via I2C
  • Arduino reads the time T2 from DS3231 via I2C
  • Arduino sends the time T2 to PC via UART. This happens at time T3 from PC's point of view.

Now the PC can estimate the half-roundtrip delay R as (T3-T2)/2. See the following chart which denotes current time form POW of PC and RTC

        |--UART--|--I2C--|--------|--I2C--|--UART--|
PC      T1              T1+R     T2+R           T3=T2+2R
DS3231  -                T1       T2              T2+R

Next, at time T4, the PC sends the value (T4+R) via UART. This will result in DS3231 to be set to (T4+R), which happens exactly R time after T4, so that both clocks are now set to the same value.

Estimation of R can be repeated several times. In this case you can calculate the standard deviation which tells you how much "jitter" your clocks have.

  • This is the way I'll go for my project, I think it is a cheap way to get a correct precision of a millisecond :) – Storca Jul 11 '18 at 16:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.