WWVB Receiver with Arduino

How to Receive 60 KHz Time Signals with Arduino Due and ES100 Module

Keith Greiner

December 4,  2023


This article describes how to receive WWVB 60 Khz time signals using the Everset ES100 module with an Arduino Due microcontroller.  The project was inspired by my interest in the subjects covered and not any financial interest in the ES100, Arduino products, or product vendors.    

Other projects by Keith Greiner:  https://sites.google.com/site/excelstatisticsguide/, and https://sites.google.com/site/keithgreiner/



Governments throughout the world maintain and disseminate a variety of standards needed for scientific research, navigation, commerce, radio and television broadcasting, and daily living in general. The standards include weights, distances, frequency, and time. This article is about the time standard. In the United States, standard time originates at an atomic clock and is disseminated by the National Institute of Standards and Technology (NIST).   For information on the U. S. standards programs, visit https://www.nist.gov. To read the fascinating history of the importance of time, for navigation, read Longitude by Dava Sobel.)  


Since 1919 the WWV radio stations have served as a platform for research on radio communications, standard frequencies, and standard time. The stations were on the air prior to the first commercial radio station; KDKA in Pittsburg. KDKA began broadcasting in 1920.  A more detailed summary of the interesting history of WWV may be found at: https://www.nist.gov/pml/time-and-frequency-division/time-services/history-radio-station-wwv.  

Information about time and frequency services may be found at: ,https://tf.nist.gov/general/pdf/1969.pdf,and https://www.nist.gov/pml/time-and-frequency-division/time-services/wwv-and-wwvh-digital-time-code-and-broadcast-format. Information about WWVH, in Hawaii, may be found at:https://tf.nist.gov/stations/wwvh.htm.  


The WWV high frequency (HF) signals include voice announcements, clicks to mark each second, and amplitude modulated (AM) voice announcements of the current time.  The signals include standard audio frequencies, as well as digital signals. HF transmitters operate on 5.0 Mhz, 10 Mhz, 15 Mhz, and 20 Mhz.  Experimental signals are also transmitted at 25 Mhz.  Synchronized transmitters are located in Ft. Collins, Colorado, and Kauai, Hawaii.  The Hawaii transmitters have the call sign WWVH and are near-perfectly coordinated with the Ft. Collins signals.  If you are listening on the HF frequencies, it is possible for you to hear announcements from a male voice from WWV and a female voice from WWVH.  


Precise time may also be obtained on the internet at www.time.gov and via several encoded web servers.  The radio broadcasts may be heard via telephone connections to (303) 499-7111 for WWV (Colorado), and (808) 335-4363 for WWVH (Hawaii).  Callers are disconnected after 2 minutes. Note that these are not toll-free numbers, although your calling plan may not charge extra.  The NIST time service had its start with the U. S. Naval Observatory because time was critical navigation   The Naval Observatory continues to support a telephone service at (202)-762-1401 and at this link.   


WWVB signals are on the very low frequency (VLF) of 60 Khz (also known as 60,000 Hertz (Hz), and 60,000 cycles).  The signals are transmitted in a digital form of amplitude modulation and pulse-width modulation (AM/PWM) plus a new phase shift modulation (PM) system. The various types of modulation are designed so that they can be sent on the same carrier without interfering with each other.  The phase shift system is the most recent addition and is used to help the signal be more accessible, despite interference from natural atmospheric sources and artificial sources that come from human activity.


Today, millions of radio clocks receive the older AM/PWM version of the WWVB signal.  The new Everset ES100 design receives and decodes the 60 Khz phase-modulated signal.     


For computer and electronics enthusiasts, the ES100 may be the easiest to use because Everset designers created an Arduino program that is specifically designed and tested for this receiver.  All you need is to know exactly how to set up the device and the software. Once you have it running, you can modify the Arduino code to perform a number of creative tasks.  


I operated an ES100 with the companion Xtendwave program over 100 times, and found that the program returned a time signal within an average of 137,390.39 milliseconds (2.29 minutes) with a distribution that has a standard deviation of 345.51 milliseconds.  Figure 1 shows the distribution response times in milliseconds.  The blue bar-graph shows the distribution of actual response times and the red line shows what a normal distribution would look like if it had the same mean and standard deviation as the actual data. The distribution suggests that if we were to replicate the analysis hundreds or thousands of times, the distribution of response times would look much like the red, normal, distribution.

The analysis of response times excludes tests made between 10 to 16 minutes after the beginning of each hour, and 40 to 46 minutes after the beginning of each hour. Image 2 shows the hourly time blocks when reception of a time signal should be avoided.  The blue sections are used by WWVB to send messages that are not decoded by the ES100.  The yellow sections represent times when a time signal may not be fully received within 137,390 milliseconds, if the Xtendwave program is started.  If you first start the program too close to one of those blocks, you might think the program is failing to run, when in fact, it’s just within a block of time when a time code is not transmitted.

If you run the program so that all or part of an average response time of 137,390 milliseconds, plus two standard deviations (691 milliseconds) from the average, fall within either of the two six-minute blocks, then the program is less likely to return a time code.  For me, that suggested I should avoid running the program during either of two blocks of time. The first begins at 7.7 minutes after the hour, and ends at 18.3 minutes after the hour. The second begins at 37.7 minutes after the hour and ends at 48.3 minutes after the hour.   When my tests were run in all or part of these blocks of time, the program returned either no value or a value that required more than four minutes to decode.  The availability of time codes for only 38.8 minutes during each hour, suggests that it is best for a local clock to use the signal as a way to regularly reset the local clock rather than serve as the clock.   A local clock might use the ES100 to check on the WWVB station once or twice an hour, or once or maybe even once a day.  Programmers should also consider that time of day, severe weather conditions, and location of the antennas can all disrupt the signal. At my location, reception is better in the evening hours.  

One way to monitor the amount of time required to obtain a time value is to examine the output from the following statements: 

Serial.print("Count in while(irq_status != 0x01) = ");


The two statements report the number of times that the program goes into the loop that waits for an interrupt request.  As long as the irq_status is not 1, the function will cycle through this loop.  The number of cycles is reported by these lines.  For my tests, most of the runs received an irq_status of 1 within a count of 1.  Some, however might take several more.  Be patient and wait a while before giving up.  If the count reaches 5, try restarting the program if you are outside the blocks where a time cannot be received.

Strategically placed Serial.print(…) statements can reveal clues to the way the program operates.  Below are output from two print statements of the hour, minutes, and seconds.  The first is printed immediately after the interrupt value of “1” is received, and the second is moments later.  

1.  4:9:88

2.    4:10:28.0000

The first line says the time received from WWVB is 4 hours, 9 minutes, and 88 seconds.

The second report says the time received is now 4 hours, 10 minutes, and 28 seconds. 

We see that 88 seconds, less 60 seconds, is 28 seconds. From this we can add one to minute to the time of the first print-out and subtract 60 seconds from the 88, leaving 28 seconds.

This suggests that the seconds register was first accumulated beyond the 60 seconds of a minute, and the minutes register was then advanced by 1 and 60 seconds were subtracted from the seconds register to leave it at 28.   

How to Use the ES100 with an Arduino Due


Items you will need include:

Following are steps that should get you up and running. The steps are for the Arduino Due.  The Due operates with a 3.3 volt logic system and provides a seamless connection to the ES100 module.  The Uno operates with 5.0 volt logic connections and requires voltage conversion for all digital channels. After I complete some tests with an Arduino Uno, I will post that information.  Until then, the following steps will get you running on an Arduino Due.


1.  Install the Arduino IDE that is available at: https://www.arduino.cc/en/main/software.

2.  Take some time to become familiar with the IDE and make sure that you have the latest updates.

3.    Obtain an Arduino Due (Not the same as a Duemilanove). I prefer to have the Due with header connectors.

4.    Install drivers for the Arduino Due.

5.    Drivers may be found at the following links, beginning with a running Arduino IDE.  Arduino IDE àBoard àBoards Manager àClick to download the following: “Arduino Sam Boards( Cortex M3 ) by Arduino 1.6.12” or greater.

6.    The listing will show as “Installed” after installation is complete.

7.    In the board selection menu, select the board “Arduino Due Programming Port.”

8.    In the port selection menu, select the serial communications port where the Arduino Due is connected.  If you have several communications ports, you will need to change this selection each time you use a different port.  Also, be sure you enter the name correctly.  If necessary, consult the documentation for your computer to find out the exact name of the communications port.  

9.    Prepare to use the ES100 kit, including the two antennae from universal-solder.com. (See: https://universal-solder.ca/product/everset-es100-cob-wwvb-60khz-bpsk-receiver-kit-with-2-antennas/)

10.  Take some time to become familiar with the documentation provided with the ES100, and look at the Everset-tech web page at this link.

11.  Download and install the Xtendwave program for the ES100.  The program may be found under the “Links and Downloads” section of the product information page. The link is titled "Download Documentation Package Incl. Arduino Code."  There are 10 items in the package.  The Arduino code is in a directory titled “ES100 Arduino” and is in a program file named “ES100_Arduino.ino”.  The program can be opened in the Arduino IDE.  

Creator(s) of the ES100 sample code indicated that they expect their code to be modified and amended by users.  My code, with amendments, may be found here: 

12.  Connect the ES100 to Arduino Due using the six connections shown below.  Below is the table that I use as a reminder of the connections and to make sure I am making the correct connections between the ES100 and the Arduino Due.  


Following is a summary table showing only the ES100 Pins, the matching Arduino Due pins and the color coding I am using on my wires. 

The following diagram shows the wire connections in a little different manner. 

13.    Make sure the Xtendwave program is coded correctly.  Do this by examining the program provided at the link shown above.

14.    Locate the following code and make sure the code in your program matches the following. The code specifies pin 22 for the enable (EN)connection and pin 24 for the interrupt request (IRQ) connection. 


// MCU constants - USER TO MODIFY



// GPIO pins

#define GPIO_EN  22  

#define GPIO_IRQ  24

15.    The Arduino Due uses a mini USB connector that is in the Program Connector.  That is the one nearest to the black external voltage connection plug.  The plug is not used in this example because the USB provides all the needed power. 

16.    You will need a USB connection cable that has a mini plug on one end and a USB-A plug (or whatever matches your computer) on the other end.  The most recent Apple MacBook Pro computers may need an adapter from the USB-A plug and the apple plug. 

17.    Arrange the two antennas so that they are at right angles as shown in the photo. I used two pieces of blue tape to secure the antennas.


18. Upload and run the program. Before opening the Arduino terminal window, be sure the upload status (Look in the bottom-right corner of the upload area), reports that the upload is complete.

19. Be patient. The ES100 requires an average of 2.29 minutes (137,390 milliseconds) to connect and retrieve the time.  That average applies to all sampled time-periods except for six-minute periods beginning at 10 minutes after the hour and 40 minutes after the hour. See the discussion above

Sample Output


Below is a sample output from the program with my amendments.  The output includes a number of diagnostic statements that I added, so I could see the flow of calculations through the program. The diagnostic statements were added when I was trying to diagnose a problem. I left the statements in the program because they provide helpful feedback on the operation and help show how the output signals might be used.   


21:36:50.793 -> Done with es100_start_rx, starting while(irq_status != 0x01) 

21:36:50.860 -> Count in while(irq_status != 0x01) = 0

21:36:50.893 -> IRQ status going in to wait for irq_status(...)... HEX = 0 ---- DEC =  0

21:36:50.962 -> Waiting for interrupt ... 

21:36:50.997 -> Inside es100_wait_for_irq()

21:39:09.821 -> Interrupt received ...


21:39:09.821 -> Reading in i2c_read(...) function. 1   1

21:39:09.821 -> IRQ status = 0x1


21:39:09.821 -> IRQ status 0x01 received, now outside of while loop in function es100_receive(int dt_array[ ]) --------------------

21:39:09.821 -> Elapsed time to IRQ status 0x01 received and outside of while loop in es100_receive(int dt_array[ ]) = 136362

21:39:09.821 -> Date and time values that are in dt_array

21:39:09.821 -> Reading in i2c_read(...) function. 1   1

21:39:09.821 -> Reading in i2c_read(...) function. 1   1

21:39:09.821 -> Reading in i2c_read(...) function. 1   1

21:39:09.821 -> Reading in i2c_read(...) function. 1   1

21:39:09.821 -> Reading in i2c_read(...) function. 1   1

21:39:09.821 -> Reading in i2c_read(...) function. 1   1

21:39:09.821 -> Reading in i2c_read(...) function. 1   1

21:39:09.821 -> Reading in i2c_read(...) function. 1   1

21:39:09.821 -> Reading in i2c_read(...) function. 1   1

21:39:09.821 -> Reading in i2c_read(...) function. 1   1

21:39:09.821 -> Disabling the ES100 and leaving function es100_receive(...

21:39:09.821 -> Past receive time from WWVB statement

21:39:09.821 -> Received UTC time HEX = 2019:3:5 3:39:6


21:39:09.821 -> Received UTC time DEC = 2025:3:5 3:57:6


21:39:09.821 ->  -- Minutes & seconds as decimal minutes = 5.1000 


21:39:09.821 -> Second boundary occurred at timer count = 136291


21:39:09.854 -> Status register = 0x1


21:39:09.854 -> Next DST transition = month 3, day 10, hour 2


21:39:09.854 -> Elapsed time to here...137311

21:39:09.854 -> Elapsed in minutes... , 2.29 , Milliseconds , 137311

21:39:09.854 -> YYYY:MM:DD:hh:mm:ss.nnnn


21:39:09.854 -> 7E9--7E9:3:5:3:39:6.0000000000000000 

21:39:09.854 -> 2019--2019:3:5:3:57:6.0000 -- Minutes & seconds as decimal minutes = 57.1000 

21:39:09.854 -> ==================================================================


Thank you to Oren Eliezer of Everset-tech and Volker Forster of universal-solder.com.

Their work made these projects possible.