BLE Time Synchronization between devices

Hello


We have an application that runs on an nRF52833, and communicates with an iOS app via BLE.
The iOS app connects to a number of nRF52833 devices.
The problem we are trying to solve right now is Time Synchronization.
After connecting to all of the devices, the iOS app wants the devices to start a certain task at the same time.
It has to send the message separately/sequentially to each device.
It would make sense for the iOS app to tell each of the devices to "start task at time T".
So the problem is to get all the devices synchronized with the iOS app, so that they all know when time T is.


One method I found, called the Cristian synchronization algorithm, is implemented as follows:

  • The slave sends a Time Synchronization Request, at time T1.
  • The master receives this request, and sends its Clock Time Tm to the slave
  • When the slave receives the master clock time, at time T2, it sets its own clock to time Tm + (T2-T1)/2


However, I also came across articles which suggest that experimental results show that application of the Cristian algorithm as described above does not give reliable results in platforms such as BLE.


Another method I came across is based on the BLE “connected event” generated by both the scanner (iOS app) and the advertiser (nRF52833 device), when the connection request is sent, (iOS app) and received(nRF52833 device).

The author of an article describing this method seems to suggest that the difference between these events is just under 1 ms, which would be probably be good enough for us.


Do you have any suggestions on time synchronization between multiple devices?

Parents
  • Hi Garrett, 
    Regarding synchronization between devices, I would suggest to have a look at this guide: Wireless timer synchronization among nRF5 devices 

    It proposes a way of sending proprietary time sync ESB packets between nRF5 device to do synchronization. 

    The biggest challenge with time sync with BLE is that BLE is a reliable protocol, meaning you have no control if the packet has been retransmitted or it's the first transmission. 
    Using the Connect Request for synchronization is a good idea because the Connect Request is not re-transmitted. However one of the fundamental challenge with time sync is the drifting when counting time between devices due to the tolerance of the oscillator. If you use Connect Request to sync the time, after a short period of time (e.g few seconds) the two device will start to drift from each other and you will most likely need to disconnect and connect again to maintain a certain time sync accuracy. 

    What described in the guide above is to periodically send a time sync beacon to all nodes to sync the time. This way we can maintain the time synchronization between nodes. 

    The challenge in your case is that you send the command from the phone which obviously can't do proprietary. But what you can do is to use one of the node as the "time keeper" this node will send the time sync beacon. Since all nodes are synchronized, they can do advertising to tell the current time it has (I assume you don't need accuracy down to micro second). The phone can scan these advertising packets and have an idea of the current time these nodes are sync to and can give the command on what T time the nodes should perform the command. 

  • Hi Hung Bui,

    Thank you for your response.

    Here is a quick overview of our application’s use case:


    The iOS app is the Master, and there are two nRF client devices.
    A short time after connection, the master instructs both clients to start their data sampling/logging.
    Ideally, they start at the same time (hence the Time Synch requirement).
    Each slave samples its sensors every 4ms, and adds the data to a log, with an incremental log entry counter.
    This data is also sent to the master, along with the log entry counter/index.
    The synchronization of the BLE packets is not essential.
    Also, withing a few seconds of being instructed to start sampling/logging, one or both slaves might move out of BLE range, and the connection(s) may be dropped.
    The slaves will continue to sample/log every 4 ms.
    When a slave comes back in to range, and BLE connection is re-established, the transmission of data can resume, with some catch-up.
    The iOS app will eventually tell the two slaves to stop logging, but will keep the BLE connections alive until all outstanding packets have been received.

    So, I don’t think I can go with an implementation that relies on all the slave devices being constantly connected to the master.
    Instead, I think I need to go with the initial time synchronization based on the BLE Connect Event.


    The amount of drift that will occur over the space of a few minutes is acceptable, and if necessary, I think I can write some software to compare both slave logs, after the fact, and stretch / shrink the data to align the samples. The crucial thing is for the sampling to start at as close the same time as possible.


    So with that all said, if I were to go with an implementation that is based on the BLE Connect Event, do you know how my nRF code could access the BLE Connect timestamp?

    Right now, I am using an nRF52DK to write some test code. My initial project is based on Nordic’s Peripheral UART example.

    Thank you

    Garrett

Reply
  • Hi Hung Bui,

    Thank you for your response.

    Here is a quick overview of our application’s use case:


    The iOS app is the Master, and there are two nRF client devices.
    A short time after connection, the master instructs both clients to start their data sampling/logging.
    Ideally, they start at the same time (hence the Time Synch requirement).
    Each slave samples its sensors every 4ms, and adds the data to a log, with an incremental log entry counter.
    This data is also sent to the master, along with the log entry counter/index.
    The synchronization of the BLE packets is not essential.
    Also, withing a few seconds of being instructed to start sampling/logging, one or both slaves might move out of BLE range, and the connection(s) may be dropped.
    The slaves will continue to sample/log every 4 ms.
    When a slave comes back in to range, and BLE connection is re-established, the transmission of data can resume, with some catch-up.
    The iOS app will eventually tell the two slaves to stop logging, but will keep the BLE connections alive until all outstanding packets have been received.

    So, I don’t think I can go with an implementation that relies on all the slave devices being constantly connected to the master.
    Instead, I think I need to go with the initial time synchronization based on the BLE Connect Event.


    The amount of drift that will occur over the space of a few minutes is acceptable, and if necessary, I think I can write some software to compare both slave logs, after the fact, and stretch / shrink the data to align the samples. The crucial thing is for the sampling to start at as close the same time as possible.


    So with that all said, if I were to go with an implementation that is based on the BLE Connect Event, do you know how my nRF code could access the BLE Connect timestamp?

    Right now, I am using an nRF52DK to write some test code. My initial project is based on Nordic’s Peripheral UART example.

    Thank you

    Garrett

Children
  • Hi Garrett, 
    As far as I know there is no "BLE Connect timestamp". Actually there is no concept of "time stamp" with our BLE stack. The nRF5 chip doesn't keep a real time counter. 

    The concept of time sync between nRF5 device is only that they can trigger a TIMER event at the same time by calculating the offset of their own TIMER. 

    Note that the TIMER here has no relation with the real time on the phone. 

    So even though you will have the CONNECTED event almost at the same time on the phone and on the nRF5, there is still a challenge how you can sync multiple nRF5 together by using this CONNECTED event. 

    I can propose you to do the following, from the time you have the connected event, you can start a timer to count time (can use RTC if you don't need high accuracy). This timer start when the BLE_GAP_EVT_CONNECTED occurs. After that, the phone should send a BLE packet to the device to tell what's the "time stamp" is when the connected event on the phone occurs. The device the will use this information with the timer that already started at the BLE_GAP_EVT_CONNECTED . This way you can sync all the device with the "real time" on the phone. 

    ===

    What we proposed in the blog is that you sync nRF5 together by using one nRF5 as the time keeper. Note that this doesn't requires the BLE connection to the phone to each of them. They sync as a standalone protocol. And they just need to be in the range of the timer keeper. Even if they are out of range of the time keeper, there will be some drift but they will automatically update the offset once they are in the range again. 

    This solution is used when you need high accuracy time sync between nRF5 device, in your application as you don't need high accuracy. You may not need this solution and can go with what I proposed above. 

  • Hi Hung Bui

    Thanks again for getting back to me.

    I am using the Nordic Peripheral UART application as my starting point, ruining on the nRF52DK.

    From what you suggested here:

    I can propose you to do the following, from the time you have the connected event, you can start a timer to count time (can use RTC if you don't need high accuracy). This timer start when the BLE_GAP_EVT_CONNECTED occurs. After that, the phone should send a BLE packet to the device to tell what's the "time stamp" is when the connected event on the phone occurs. The device the will use this information with the timer that already started at the BLE_GAP_EVT_CONNECTED . This way you can sync all the device with the "real time" on the phone. 

    How would I go about hooking in to the BLE_GAP_EVT_CONNECTED event?

    I can't find it anywhere in the code when I do search etc.

    Regards

    Garrett

  • Hi Garrett, 

    It's my mistake I thought you were using the legacy NRF5 SDK.
    I guess you are using nRF Connect SDK instead. 

    If it's the case you should look for the connected call back instead. It's the connected callback you register with BT_CONN_CB_DEFINE(). For example here is in peripheral_uart example: 

  • Thank you

    Yes, I am using the legacy NRF5 SDK, but I just wanted to do some testing using code in VSCode :)

Related