Awesome
nerves_time
NervesTime
keeps the system clock on Nerves
devices in sync when connected to the network and close to in sync when
disconnected. It's especially useful for devices lacking a Battery-backed
real-time clock and will advance
the clock at startup to a reasonable guess.
Installation
First add nerves_time
to your project's dependencies:
def deps do
[
{:nerves_time, "~> 0.4.2"}
]
end
Ensure that your vm.args
allows for
timewarps.
If it doesn't, nerves_time
will update the OS system time, but Erlang's system
time will lag. The following line should be in the beginning or middle of the
vm.args
file:
+C multi_time_warp
If you're using one of the official Nerves Systems, then this is all that's
needed. nerves_time
requires Busybox's ntpd
and date
applets to be
enabled. If you haven't explicitly disabled them, they're probably enabled.
Configuration
Startup
nerves_time
by default does not block waiting for a valid system time to be set.
This can result in your application running before the time has been adjusted, which
may be undesirable. To lessen the likelyhood of that happening you can adjust
the :await_initialization_timeout
config to wait for a valid system time to be set.
If nerves_time
fails to do that within the given timeframe it will stop blocking
startup and continue trying asynchronously.
# config/config.exs
config :nerves_time, await_initialization_timeout: :timer.seconds(5)
NTP
nerves_time
uses ntp.pool.org for time
synchronization. Please see their terms of
use before tweaking nerves_time
.
Alternative NTP servers can be specified using the config.exs
:
# config/config.exs
config :nerves_time, :servers, [
"0.pool.ntp.org",
"1.pool.ntp.org",
"2.pool.ntp.org",
"3.pool.ntp.org"
]
It's also possible to configure NTP servers at runtime. See
NervesTime.set_ntp_servers/1
.
Valid time range
nerves_time
has a concept of a valid time range. This minimizes time
errors on systems without clocks or Internet connections or that may have some
issue that causes a very wrong time value. The default valid time range is
hardcoded and moves forward each release. It is not the build timestamp since
that results in non-reproducible builds.
Applications can override the valid range via the application config:
# config/config.exs
config :nerves_time, earliest_time: ~N[2019-10-04 00:00:00], latest_time: ~N[2022-01-01 00:00:00]
Algorithm
Here's the basic idea behind nerves_time
:
- If the clock hasn't been set or is invalid, set it to the earliest valid
time known to
nerves_time
. This is either set in the application config or defaulted to a reasonable value that likely moves forward a little eachnerves_time
release. - check for time via a Real Time Clock
- Run Busybox
ntpd
to synchronize time using the NTP protocol. - Update Real Time Clock periodically and on graceful power downs. This is currently only done at around 11 minute intervals.
To check the NTP synchronization status, call NervesTime.synchronized?/0
.
Real Time Clock
A hardware based real time clock can be configured by added a config.exs entry:
config :nerves_time, rtc: {SomeImplementingModule, [some: :initialization_opt]}
By default Nerves Time is configured to use NervesTime.FileTime
which will
Check for ~/.nerves_time
. If it exists, advance the clock to it's last
modification time.
See the documentation for NervesTime.RealTimeClock
to implement your own
real time clock.
Credits and license
This project started as a fork of nerves_ntp by Marcin Operacz and Wojciech Mandrysz. It has quite a few changes from since when they worked on the project, but some of their code still exists. Both their project and this one are covered by the Apache-2.0 license.