There are some beautiful radio-controlled watches available these days from Citizen, Seiko, Junghans, and even Casio. These timepieces don’t need fiddling every other month, which is great if you have more than one or two and can never remember what comes after “thirty days hath September…”
In the US, these watches work by receiving a 60-bit 1-Hz signal on a 60-kHz carrier wave broadcast from Fort Collins, Colorado called WWVB. The broadcast is quite strong and generally covers the entire continental US, but some areas of the country can have unreliable reception. I live in the SF Bay Area in an area with high RF noise and my reception can be spotty. My watches sync often enough that it’s not an issue 363 days out of the year, but sometimes they can miss DST shifts for a day or two. The east coast is known to be even more challenging. And people who live in other countries such as Australia have generally been out of luck.
Wouldn’t it be great if anyone anywhere in the world could set up a home transmitter to broadcast the time so their watches were always in sync?
WWVB has been around awhile and there have been various other projects (1,2) that have demonstrated the feasibility of making your own WWVB transmitter. But these all had very limited range. I wanted to build something that could cover my whole watch stand and be based on a more familiar toolset for the typical hobbyist, namely USB-based 32-bit microcontroller development boards, WiFi, and Arduino. My goal was to make something approachable, reliable, and attractive enough it could sit with my watch collection.
Is this legal?
The FCC requires a license to transmit, but has an exemption for 60 kHz transmitters as long as the field strength is under 40 μV/m at 300 meters. You will definitely not exceed this limit 💪🏼
About WWVB
The classic WWVB transmits one bit of information per second (1Hz) and takes one minute (60 bits) to transmit a full time and date frame.
An example
Here’s an example of one 60 second time encoding (graphic designed for "light mode"):
You can see that the minute of the time is encoded in the first 10 seconds of the window. The hour is in the next 10 seconds, the day is between seconds 22 and 33, etc.
If we wanted to indicate that the time was 30 minutes past the hour, for example, we would set bits 2 and 3 (20 and 10) to “high”.
| Bit | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 |
|---|---|---|---|---|---|---|---|---|---|---|
| Value | 40 | 20 | 10 | 8 | 4 | 2 | 1 | |||
| Example: 30 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
Similarly, the 7th hour of the day would be 0000111 for bits 12 thru 18.
It’s a trit, not a bit.
How do we represent “high” and “low” for each 1s bit? You might think it would just be a high voltage for high and a low voltage for low, like you would use on a digital arduino pin, but that’s not how it’s done.
WWVB “bits” are actually not just 0 and 1. They’re actually "trits” because they can represent a 0, 1, or a “mark”, and this is one reason why we can’t just use a simple high/low to represent them. The mark is important to allow simple receivers to orient themselves within the signal window. Your watch knows that the last second in the window and the first in the next window are both “marks”, and so it knows to start a new window whenever it sees two marks in a row.
WWVB uses Pulse Width Modulation (PWM) to represent the three possible trit states. In a given 1 second bit, the width of the pulse determines whether the bit is a 0, 1, or mark.
- If power is reduced for one-fifth of a second (0.2 s), this is a data bit with value zero.
- If power is reduced for one-half of a second (0.5 s), this is a data bit with value one.
- If power is reduced for four-fifths of a second (0.8 s), this is a special non-data "mark", used for framing.
| Low | Trit value |
|---|---|
| 0.2s | 0 |
| 0.5s | 1 |
| 0.8s | mark |
Coming back to that original example but just focusing on the Minutes section, you can see the trits in the color encoding of the diagram.
Light blue is high and dark blue is low. You can see second 00 is dark blue (low) for 0.8s, which means it’s a “mark”. Second 01 is dark blue for 0.2s, so it’s a 0. Second 02 is dark blue for 0.5s so it’s a 1, and so on.
So now you know how to encode the time and date using WWVB!
The carrier wave
There’s one more part of the signal that needs explanation. While we’re transmitting bits at a frequency of 1 bit per second (1 Hz), we’re actually doing it on top of a 60 kHz carrier wave by varying the amplitude of the carrier wave.
Like we said in the previous section, we represent a high/low/mark trit by the width of our pulse. High for 200ms is a zero, high for 500ms is a 1, high for 800ms is a mark. The way we’ll do that on our 60 kHz carrier wave is by using PWM again, but this time on the 60 kHz signal instead of on the 1 Hz signal. We’ll use a duty cycle of 50% to represent High, which means that half of our 60 kHz pulse is high and half is low. We’ll use a duty cycle of 0% to represent Low which effectively means the whole pulse is low.
So what you'll expect to see when you're transmitting a WWVB "mark" is a 1s pulse where half of the pulse is "high" using a 60 kHz signal at 50% duty, and the other half is "low" with a 0% duty (not to scale):
Similarly, a 0 would be a 60 kHz 50% duty cycle for 0.2s, followed by a 0% duty cycle for 0.8s. And a 1 would be the reverse.
The layers of WWVB
In summary, a WWVB date/time is encoded using a few different layers of encoding. At the top is what we think of as a date/time and at the bottom is the 60 kHz carrier wave.
| A date/time is encoded as a… |
| 60-bit frame, using… |
| 1s PWM trits, on top of a… |
| 60 kHz 50% duty cycle carrier wave |
| 🐢🐢🐢 all the way down |
Now that you understand how it works, let’s build the transmitter!
Mike Burton


