Introduction: One Component Radio Clock Time Transmitter

Believe it or not, you can generate a time signal to set your WWVB controlled radio clocks with just an attiny45, wire antenna, and a battery. Upload the code to an attiny45, put a 20" or so wire on pin 6 and power with 5v.  You'll need to put your radio clock close to pick up the signal best.

Step 1: The WWVB Time Signal and 60Khz Carrier

The WWVB time signal is a 60khz carrier modulated by reducing the carrier in a particular sequence to encode the time.  Wikipedia has a good article which I used to design this project:

The attiny45/85 has a fast timer that can be set to generate a square wave at 60khz like this:

/* Initalize Fast PWM on OCR1A*/
DDRB |= _BV(PB1); // Set PWM pin as output
PLLCSR |= _BV(PLLE); // Start PLL
_delay_us(100);      // Wait till PLL stablizes p. 9
PLLCSR |= _BV(PCKE); // Set Clock source to PLL
OCR1C = 132; // Set OCR1C to top p. 91 (60kkHz)
OCR1A = 66;  // Set beginning OCR1A value (50% duty cycle)
TCCR1  |= _BV(CS12);   /* Set clock prescaler to 8   */
TCCR1 |= _BV(PWM1A)   /* Enable PWM based on OCR1A  */ \
   |  _BV(COM1A0)   /* Set PWM compare mode p. 89 */ \

Step 2: Modulating the 60khz Carrier

To modulated the carrier, just reduce the duty cycle of the wave by changing OCR1A to less than 66.  I set the other timer on the attiny45 to fire an interrupt 61 times a second like this:

/* Initalize CTC interupt on timer0 at 61hz */
TCCR0A  |= _BV(WGM01); //pg. 82  Mode 2 CTC OCR0A TOP
OCR0A |= 127;   // 8mhz / ((127+1) * 1024 prescale) = 61hz
TCCR0B |= _BV(CS00) | _BV(CS02);  // set prescaler to 1024
TIMSK |= _BV(OCIE0A);// enable compare match interrupt
sei(); // Enable interupts

This gives 61 times slices in each second to reduce the power of the carrier for modulation.  

Step 3: Encoding the Modulation With a Time Signal

Next I wrote a routine which would keep track of the 61 time slices of each second and change to duty cycle of the carrier to encode the time signal.  Each case represents a second of the minute long time signal.  Each parameter can be set.  I have a few defines that can be used to easily change the hour and minutes.  You can add other defines too.


switch (slot) {

  case 0 : { signal = 2;break;}

  case 1 : { signal = ((minute_tens >> 2) & 1);break;} // min 40
  case 2 : { signal = ((minute_tens >> 1) & 1);break;} // min 20
  case 3 : { signal = ((minute_tens >> 0) & 1);break;} // min 10

  case 5 : { signal = ((minute_ones >> 4) & 1);break;} // min 8
  case 6 : { signal = ((minute_ones >> 2) & 1);break;} // min 4
  case 7 : { signal = ((minute_ones >> 1) & 1);break;} // min 2
  case 8 : { signal = (minute_ones & 1);break;}   // min 1

  case 9 : { signal = 2;break;}

  case 12 : { signal = ((hour_tens >> 1) & 1);break;} // hour 20
  case 13 : { signal = ((hour_tens >> 0) & 1);break;} // hour 10

  case 15 : { signal = ((hour_ones >> 4) & 1);break;} // hour 8
  case 16 : { signal = ((hour_ones >> 2) & 1);break;} // hour 4
  case 17 : { signal = ((hour_ones >> 1) & 1);break;} // hour 2
  case 18 : { signal = (hour_ones & 1);break;}  // hour 1

  case 19: { signal = 2;break;}

  case 26: { signal = 1;break;}  //
  case 27: { signal = 1;break;}  // Day of year 60
  case 29: { signal = 2;break;}  //

  case 31: { signal = 1;break;}  //
  case 32: { signal = 1;break;}  // Day of year 6
  case 37: { signal = 1;break;}  //
  case 39: { signal = 2;break;}

  case 42: { signal = 1;break;}  //
  case 43: { signal = 1;break;}  // DUT1 = 0.3
  case 49: { signal = 2;break;}

  case 50: { signal = 1;break;}  // Year = 08
  case 55: { signal = 1;break;}  // Leap year = True
  case 59: { signal = 2;break;}

  default: { signal = 0;break;}

switch (signal) {

  case 0: {
  // 0 (0.2s reduced power)
  if (timer < 12) {OCR1A = 6;}
   else {OCR1A = 66;}
  } break;
  case 1: {
  // 1 (0.5s reduced power)
  if (timer < 30) {OCR1A = 6;}
   else {OCR1A = 66;}
  } break;
  case 2: {
  // Marker (0.8s reduced power)
  if (timer < 48) {OCR1A = 6;}
   else {OCR1A = 66;}
  } break;

timer++;   // Advance timer
if (timer == 61) { // Check to see if at end of second
  timer = 0;   // If so reset timer
  slot++;   // Advance data slot in minute data packet
  if (slot == 60) {
   slot = 0; // Reset slot to 0 if at 60 seconds
   minute_ones++; // Advance minute count

Step 4: The Hex Files and C Code

The hex files for direct uploading of your microcontroller can be downloaded below.  Also included is the C source file.

Have fun!


NTMMFTS made it! (author)2016-06-04

Hi, I tried this with a Digispark but it wouldn't set my Casio Wave Ceptor Multiband 6 watch - always errors out. The antenna wire is in pin 6 - P1 on the digispark - with 5v from the Digispark 5v pin connected to the antenna near the board - see attached picture. It's definitely modulating at just below 60 kHz as tested with a sperry multimeter that has a hz function, but not sure how sensitive this watch is when the signal is about 1 kHz off. Also not sure if there's any interference from the USB connection itself which is how I'm powering everything. Any ideas why it's not working with a Digispark? I have a bare atmega16 on the way which I intend to try on a breadboard like your project, so hopefully that will work. I'd rather have the USART instead of using softserial and the extra flash space so I can translate gps or ntp time for modulation on the AVR instead of having to use a companion program due to the limited space. Thanks for this example even if you don't have any comments about it not working on a Digispark :-).

sbmull made it! (author)sbmull2016-06-04

Thanks for trying out my code! I have a solar powered wavecepter too. It is the best watch I've ever had. Works flawlessly and never needs batteries.

You can always for your signal on a scope just to make sure things looks like the ought to. Also, you might want to try a longer wire for an antenna. The signal is very low, 60khz, and does not get much energy out of a non-resonant antenna. The longer the wire, the closer it will be to resonant and the more RF you will get out.

Also, the 60khz is being generated from the on board RC oscillator which can drift. You can calibrate the oscillator to about 1% or so of true value if you need to. See AVR053 from Atmel.

Have fun!

NTMMFTS made it! (author)NTMMFTS2016-06-05

Haven't tied this yet but I'm going to, it's called Poor Man's Tiny Tuner that simplifies the oscillator tuning process with ready-made sketch for attiny. . The author states that the oscillator can be as much as 10% off from the factory, which may explain the inaccuracy issues I had with the Digispark.

NTMMFTS made it! (author)NTMMFTS2016-06-04

Thanks for the tips... Looks like the (or my) Digispark is just too unstable, keeps fluctuating a couple of kHz no matter what I do with the antenna or code values, so I'll probably have to read up on oscillator calibration if I want to stick with the Digispark. The real proof is that my wave ceptor second hand never moves to the W (work/stable) while receiving :-(. Yes, great watch, mine is a Japan model, all titanium with a white face and sapphire crystal - it's gorgeous like a Tag Twin Time but with a ton of features :-).

imnlfn made it! (author)2016-03-25

I'm well on my way to implementing a device similar to this using a Particle Photon that can retrieve the current time via NTP. This article was a big help in that effort. It's pretty much my primary reference, along with the Wikipedia article on WWVB.

I would like to point out, though, that the code block for the switch statement is incorrect. Everywhere it says ">> 4", it should say ">> 3". Otherwise, those slots will always evaluate to zero.


rpourzia made it! (author)2014-03-23

Great Article. What is the reason for slicing the second into 61? I would have thought that you want to slice by 10 and change the duty cycle for 8 slices for marker, 2 slices for 0, and 5 slices for 1.


sbmull made it! (author)sbmull2014-03-23

That is a good question. Look in the code comments and you will see this calculation: 8mhz / ((127+1) * 1024 prescale). That yields 61.05. So by setting the counter to 127, you get an integer number of slices at an adequate resolution. There are other solutions.

Rezer made it! (author)Rezer2015-05-20

I'm sure it's not an issue, but that .05 bothers me. That means that every minute your modulated signal differs from an actual minute by (.05*60)/61 seconds, or roughly 50ms per minute. Meaning, if you started out in sync with WWVB, after 20 minutes you'd have transmitted 1199 bits when you should be at 1200, and WWVB would be 1 bit ahead. I'm sure the markers bring everything back in sync with anything receiving, but ideally the frames would add up to exactly one minute. This can be accomplished with a prescale of 512 and counter of 125, giving exactly 125 interrupts per second. Realistically the internal resonator isn't accurate enough to matter, but hey...whole numbers are prettier :p

rpourzia made it! (author)rpourzia2014-03-23

Thanks. I love the simplicity and the elegance of this. As you know, depending on your location and how the radio is facing, you don't always get the optimal WWVB signal. This can be annoying right after the DST change where one or more of radios stay behind. This is a great way of nudging them to the right time until they catch up.

BaldvinH made it! (author)2015-04-02

Tnx for this excellent piece of code and description. I wonder, is this something one could port to or re-implement using Arduino, like the pro-mini or similar, for example? Do you know of any such implementations?

sbmull made it! (author)sbmull2015-04-02

Yes I think so. Almost any microcontroller with PWM should be able to do it. I do not know of any other implementations with AVR's (like the arduino), but it would be possible.

DawnLagdao made it! (author)2014-11-06

I want to try this project for my watch. What are the needed parts? Software?

tz1 made it! (author)2014-04-03

Go farther, start with a GPS with 1PPS output, and you can be your own WWV, even where the WWV signal can't reach. Second, you could use something like a neopixel to flash the timecode (and a higher resolution tick), aligned to the UTC second so you could also timestamp video with high accuracy. (Or send tones like the 5/10/20Mhz time services do).

sbmull made it! (author)sbmull2014-04-03

You are more than welcome to extend this project. For myself, adding more would subtract from its beauty-elegance-simplicity.

BillL2 made it! (author)BillL22014-09-10

I was thinking about synchronizing clocks on a sailing boat using this idea. I have several brass clocks with cheap mechanisms I could replace with cheap radio controlled mechanisms but they would not get synchronized out to sea. Also maybe it would be nice to set them to local time. The boat has a computer connected to GPS so there is a time signal. Soon after thinking about this I thought maybe some one has done it and then found your article. Well done! I would be interested if anyone has extended the project to take time from GPS.

linuxuser2 made it! (author)2014-04-20

Sorry for this elementary question, but how are you making the 60kHz signal with Timer1?

I am assuming that using the internal oscillator, you start with 8 MHz and you use a prescale of 8. So the timer clock is at 1 MHz, right?

So each count is 1 microsecond. So with 66 as OCR1A, and clearing the counter with 132, don't you toggle PB1 at 66 us?

Sorry for the noob question, and thanks in advance.

sbmull made it! (author)sbmull2014-04-21

No need to apologize! In this case the Datasheet is your friend. Check out page 91 of the datasheet for the attiny85. There are listed there settings for timer1 for 20khz to 500khz. For 60khz, it is PCK/8 and OCR1C=132. The formula is Fpwm = Fclk/(OCR1A+1) (page 90).

Note that the PLL is being used, so the base clock is at 64mhz. We are using a 8 prescaler so the clock for the timer is 8mhz.

8mhz/(132+1) => 60,150hz.

I hope that helps and thanks for your interest in my project!

hberg32 made it! (author)2014-03-25

This is really cool. My first thought was "ok, now how do we get it to pull the current time from a timeserver so we don't have to hardcode the time" and brought the code over to the Arduino IDE to see if I could translate the code for an Uno (atmega328). After googling the myriad errors I understand now what PLL is and see that the atmega828 doesn't support it at the hardware level. Does this mean one would have to use an algorithm in place of the hardware PLL or is this chip just unsuitable for the task? If so, do you think the attiny45 or 85 have enough pins to interface with an ethernet shield to get the time (pardon my ignorance, I'm not familiar with the attiny).

sbmull made it! (author)sbmull2014-03-26

Thanks for your comment! I see no reason why the code would not work on an atmega. The critical part is getting the timer to pulse at 60Khz. You will need to tinker with the timer settings on the atmega to find a configuration that works.

Also, if the ethernet shield code uses interrupts, you'll need to make sure they do not interfere with the time code modulation interrupt.

If the ethernet shield uses spi, and I think it does, you only need three datalines, and that many are still available on the attiny. So I would say, it is possible... if you can get the code to work in the dataspace.

Sounds like a fun challenge!

hberg32 made it! (author)hberg322014-03-26

So it doesn't necessarily need PLL as the clock source?

sbmull made it! (author)sbmull2014-03-26

Yes, the PPL is not critical. It only provides an elegant and simple way to generate a 60kHz squarewave. Also, you can tweek a couple of numbers and make it a 1, 2 or 4 Mhz transmitter too ;). At 1Mhz you can listen to the signal on an AM or shortwave radio.

rdk1207 made it! (author)2014-03-22

Whats the usb ISP you're using to program the attiny??

sbmull made it! (author)sbmull2014-03-25

It is a bus pirate. I like it because I can use a terminal interface to turn the power on and off. I also use a homemade USBtiny which is faster for programming. Mine is an older version that has a case. The newer ones are plain pcb's with headers.

eried made it! (author)2014-03-22

Amazing! how about receiving the signal back cheapely?

sbmull made it! (author)sbmull2014-03-22

You'll need a WWVB radio clock. Most department stores have them for about $10 or less. Thanks for the comment!

eried made it! (author)eried2014-03-22

Sure but no ultra smart/cheap way like the one you did with the tiny? The radio module is usually similar priced to the gps module so I often forget about using radio for clock sync (gps module is almost the same price range 20-40 usd and offers the synchronized clock)

sbmull made it! (author)sbmull2014-03-22

The WWVB signal almost requires some sort of ferrite rod coil as an antenna. The antenna is the killer when it comes to price. The electronics are not very much.