Bluegiga Forums/Knowledgebase/Bluetooth Smart

[HOW-TO]: Implement a real-time clock (RTC) on a BLE112

Jeff Rowberg
posted this on April 27, 2013 02:25

There is no real-time clock built into the BLE112/BLE113 modules, so if you need to keep track of the time, some functionality has to be implemented externally in your application. There are two options for adding real-time clock functionality to the BLE112/BLE113, depending on the kind of information you need and what you will be doing with it.

Option 1: External RTC module

One option is to use an external RTC chip, connected over either SPI or I2C (best to use SPI for the BLE112 though I2C may work, and either is fine for the BLE113 due to its hardware I2C interface). This is the only real option if the application needs to be aware of the actual date and time, day of the week, leap years, etc.

Suitable modules for this purpose include the Maxim DS1307 (I2C) and the Maxim DS3234 (SPI), among many others.

Option 2: UNIX timestamp and internal timer

The other option is to use a UNIX timestamp value and a 1-second soft timer to increment it. The UNIX timestamp format is an unsigned 32-bit integer which contains the number of seconds since midnight on January 1, 1970. It is therefore very easy to keep track of, once you have initialized a counter with a "now" reference value. This initialization would need to be done by a remote device, for example by an iPhone connecting to the peripheral device the first time (or re-syncing upon each connection). After this initialization, the BLE’s own timer can be configured on a 1-second interval and set to add 1 to the counter variable on each "tick."

The 32.768 kHz crystal on the BLE112/BLE113 modules has 30 PPM accuracy, so this should remain on-time for the life of a typical BLE sensor. 31.5 million seconds per year means that it could get up to 31.5*30 = 930 seconds = ~15 minutes off over the course of a whole year, assuming that it is only initialized once and no re-sync ever happens.

The BLED112 dongle does not have an external 32.768 kHz crystal, and instead uses the internal 32.000 kHz oscillator which is not as accurate (~2000 ppm instead of 30 ppm). If you use this approach on a BLED112 dongle, make sure you change the interval constant from "32768" to "32000", and realize that the accuracy will be significantly lower, especially over longer periods of time.

In a BGScript project, this requires a couple of things. First, we need a GATT characteristic which we can use to initialize the timestamp from an external source. The following XML code can be used in gatt.xml, for example:

<service uuid="64e9837d-ca48-48c4-bb71-323e5a85b51f" id="timesync_service">
    <description>Time Sync Service</description>
    <characteristic uuid="51686b01-7b48-4675-891c-ddc7c4b36e66" id="c_timesync_reference">
        <description>Time Sync Reference</description>
        <properties read="true" write="true" notify="true" />
        <value length="4" type="hex" />
    </characteristic>
</service>

Second, we need the appropriate mechanism in the BGScript code to set up the timer and accept the initialization value if it is supplied. The code below additionally writes the incremented time value back into the characteristic on each tick, so that a remote client may subscribe to notifications and get a constant "tick" value (this is optional though).

dim tick            # tick counter
dim timestamp       # 32-bit UNIX timestamp value offset from host device

# catch system boot/reset event
event system_boot(major, minor, patch, build, ll_version, protocol, hw)
    # default timestamp value (January 1, 2000 @ 00:00:00, for verification)
    timestamp = 946684800

    # configure timer for 1-second intervals
    call hardware_set_soft_timer(32768, 0, 0)

    # put module into discoverable/connectable mode
    call gap_set_mode(gap_general_discoverable, gap_undirected_connectable)
end

# catch disconnection event
event connection_disconnected(handle, result)
    # put module back into discoverable/connectable mode
    call gap_set_mode(gap_general_discoverable, gap_undirected_connectable)
end

# catch timer tick, once per second
event hardware_soft_timer(handle)
    # increment tick counter
    tick = tick + 1
    
    # update GATT time sync characteristic
    call attributes_write(c_timesync_reference, 0, 4, timestamp + tick)
end

# catch remote write of characteristic value
event attributes_value(connection, reason, handle, offset, value_len, value_data)
    if handle = c_timesync_reference then
        # update timestamp variable with new reference value
        timestamp = (value_data(3:1) << 24) + (value_data(2:1) << 16) + (value_data(1:1) << 8) + value_data(0:1)
        
        # reset tick back to zero since we have a new immediate reference
        tick = 0
    end if
end

There you have it!

 
Topic is closed for comments