Instructables
Picture of 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.



 
Remove these adsRemove these ads by Signing Up

Step 1: The WWVB time signal and 60Khz carrier

Picture of 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: https://en.wikipedia.org/wiki/WWVB

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

Picture of 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

Picture of 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.

ISR(TIMER0_COMPA_vect){

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

Picture of 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!
pwm.c3 KB
tz17 months ago

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 (author)  tz17 months ago
You are more than welcome to extend this project. For myself, adding more would subtract from its beauty-elegance-simplicity.
BillL2 sbmull1 month ago

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.

linuxuser26 months ago

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 (author)  linuxuser26 months ago

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!

hberg327 months ago

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 (author)  hberg327 months ago

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 sbmull7 months ago

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

sbmull (author)  hberg327 months ago

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.

rdk12077 months ago

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

sbmull (author)  rdk12077 months ago

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.

rpourzia7 months ago

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.

Thanks.

sbmull (author)  rpourzia7 months ago

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.

rpourzia sbmull7 months ago

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.

eried7 months ago

Amazing! how about receiving the signal back cheapely?

sbmull (author)  eried7 months ago

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

eried sbmull7 months ago
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 (author)  eried7 months ago

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.