Arduino Uno based NTP Server
Ham radio digital modes require accurate computer time. On the other
hand, appliance computer hardware
clocks do not typically maintain accurate time in the absence of periodic correction. Clock adjustments are
accomplished automatically via scheduled updates from an Internet time
what if Internet connectivity is not readily available, or
expensive, such as when operating from a remote area, perhaps a wilderness park? In such a situation it would be convenient to
synch the computer clock to an accurate time source that
does not depend on Internet connectivity.
When I first
thought of using Arduino as a time server I found this forum post, describing an Arduino
Mega2560 implementation of exactly the thing I wanted. The post
suggested that the sketch could be modified for a 328 board by
substituting software serial in place of the Mega’s UART-1. It occurred
to me that an adaptation for a wireless-enabled board might also be
feasible. However, I ordered an Ethernet shield, in case the wireless
idea didn’t pan out. The shield that I purchased is based on the older
W5100 chip (photo
The GPS used in the above-cited Arduino
Mega project was
a u-blox NEO 6M. I planned to use the NEO M8N, which in my previous
experience acquires a fix more rapidly and under less-favorable
conditions than the 6M.
While waiting for the Ethernet shield I played with a wireless board,
but encountered some sort of problem, which I’ve
forgotten. It would likely not be difficult to adapt this concept to a
wireless board. However, instead once the Ethernet shield arrived I went about
adapting the original published sketch for the Uno. This first
only the Uno, Ethernet shield, and GPS. The sketch is here.
During development I used a free NTP Server checker from Galleon
Systems for testing (right). Later, when testing the revision to be
described below I used
another free NTP Server Tool, also from Galleon
Systems. The latter displays milliseconds in the transmit timestamp
(needed to verify the fractional second calculation that would become
part of the second revision).
At first, the timestamp returned by the
Arduino server was off by a day. This turned out to be a bug in the
leap year calculation. Once that error was corrected, the server
returned the correct date/time from the GPS. I tested many times with
the Galleon Systems free checker before working up the nerve to update
a real computer from the Arduino NTP server. For this first computer
test I decided to
start with the Raspberry Pi. If testing somehow screwed it up, the Pi
would be easy to restore. The first step was to edit the file
commenting-out the four default Debian NTP servers and substituting the
IP address of the Arduino Ethernet shield (a private local area network
address). Step 2 was to reboot, which automatically obtains time from
the server. I discovered this fact by monitoring hits to the Arduino server
via its serial com channel. Raspberry Pi doesn’t have a hardware clock.
If Pi didn’t connect to NTP on startup it would be necessary to set the
clock interactively! The third step in the Pi clock update test was redundant, i.e. to force an update
through sudo service
Each hit was recorded on the server side, by enabling a conditional
debug code segment. All this is documented in the sketch itself.
I was hesitant to try updating the
Windows time clock, as I imagined a screw-up in Windows time might have
been harder to undo. Eventually though I worked up the nerve to try it.
Nothing happened—Windows time did not update. The answer was first
found in this article, and then later in
others as well. It is necessary to stop the time service and then add
the Arduino NTP server as a trusted server (illustration below). As the
title bar indicates, PowerShell was run as Administrator. (It would be
the same for the Windows command shell.) After making this change, the
LAN NTP server shows up in the list of trusted time servers, and can be
update Windows time. This procedure is illustrated
in the demo video link at the bottom of this write-up. (In case it
matters, the Windows version for this test was Windows 7.)
It bothered me that the server only
responded on the second. As designed, the original sketch checked for an
UDP packet after each one second tick of the GPS clock. Packets had
to wait between ticks. This delay was observable at the client end. In
other words, updating from time.nist.gov was typically faster than
updating from the local area network server. NIST was nearly
instantaneous, while the latter averaged around a half second
delay. I thought the polling was backwards, that UDP should
be checked all the time. Trouble was that even if a packet were
detected immediately, it would still be necessary to wait for the GPS
on-the-second time mark. The solution that occurred to me was
unnecessarily complex, and in a way flawed, but I will describe it
Real-time clock version
article I have described the DS3231 oscillator chip. This IC
is based on a temperature compensated 32 KHz crystal oscillator. The
idea was that the DS3231 could be synched to the GPS periodically, say
once an hour, and would provide time on demand whenever a UDP packet
was received. Instead of reporting zeros for the fractional part of
second, the NTP server would report the time to milliseconds accuracy.
Well, the first problem to manifest after I’d thrown a DS3231 into
the mix was that the DS3231 real-time
clock does not report
milliseconds! The Arduino itself does
have a milliseconds counter that
records elapsed time from startup. It would be possible to use that
counter, setting an application-level milliseconds counter to zero on
each GPS update. But then why do we need a real-time clock?
Having already wired in the oscillator
and incorporated code for getting its time, etc. I decided to go ahead
and implement the scheme described. In this sketch,
NTP is polled continuously and fractional time is returned immediately,
using the GPS-synced RTC for seconds, and the Arduino milliseconds
counter for fractional seconds. The Galleon Systems tool was used to
test fractional seconds, as illustrated below and in the demo video.
The significance of the highlighted
timestamp in the above illustration is that I was unsure whether the
decimal part was being
correctly encoded as an unsigned int32 until the same value was
the client side display.
Notwithstanding the rather awkward
conglomerate of time sources I fully expected this NTP server to update
Windows, just as the first simpler one had done—It did not.
Troubleshooting revealed that the problem was fractional seconds. If
the fraction was anything except zero, Windows would timeout,
displaying the message, “An error occurred while Windows was
synchronizing with <IP address>.”
The server receives the Windows packet and replies, but Windows seems
not to understand the reply format. To be sure that nothing else
about the revision mattered, I created a conditional #define
FOR_WINDOWS, which if true would zero-out milliseconds and if false
would return the full decimal value for time. When true, Windows would
accept the reply packet and process it normally. However, that bothered
as well, because the server was now reporting an incorrect time.
Whereas with the first sketch the server was replying on the second,
when 0 was the true value for milliseconds (or very nearly so).
I went back to RFC 5905 to see if maybe something
important was missing or invalid in the reply packet, but the fields
that follow the 32-bit
fractional seconds field are designated as optional, and are not
in the 48-bit Arduino-generated packet that Windows accepts. I also
Microsoft documentation about Windows time service and tools. It
was interesting to learn that the guaranteed time is not very accurate.
That is because different computers have more-or-less accurate hardware
clocks, while setting and synchronizing them is basically up to the
user. In any event, reading about Windows time did not lead to an explanation for
the fractional seconds anomaly. If I find the answer from further
investigation, then of course I will post it here.
Demo: Arduino GPS NTP Server
on this page are intended for entertainment only.
The author makes no claim as to the accuracy or completeness of the
information presented. In no event will the author be liable for any
damages, lost effort, inability to carry out a similar project, or
to reproduce a claimed result, or anything else relating to a decision
use the information on this page.