2009-06-20

The Arduino - CMMR-6P-60 "Almost Accurate Clock"

Well, the machining, shimming and tuning took hold and we've got something that will output UTC, 4 US time zones and the date. The temperature readout is Celsius, but with a change of a chip, and a little code, we could have Fahrenheit.

Yep, It's Alive!

Major code modifications were as follows:

* Decode WWVB data stream, Short impulse noise filtering, Drop bad frames, * Display: Signal Quality Indicator, Month & Day from Day of Year data, * Time Zone & DST Indicator for Local Time Display. Extend clock function * to increment day and year for midnight rollover and year end rollover. * * Choose UTC/Local Time, Choose between four US Time Zones for local time * * Note that UTC time display and Time Zones are set with DIP switches, flip settings * and hit reset button. Polling switches to dynamically change UTC/LocalTime/TZ messes * up the receiver decoding by introducing oddball delays that make it mistime the falling edge. * Dip-One 0=Local, Time 1=UTC | Dip-Two&Three 00=E, 01=C, 10=M, 11=P * * Frame Error Indicator - serves as signal indicator - Arduino serves as clock * during the noisy parts of the day, as it starts picking up WWVB frames, it syncs * to NIST time. * 1. Continuous full block indicates time as accurate as propagation delay will allow. * 2. Block and half block alternating. Signal is degrading, sync update when block shows. * 3. Half block, 5 or less sequential frame errors, low bar more than 5 frame errors. * 4. Low bar continuous, poor WWVB signal, no sync. Running on microprocessor clock only. * Your time will only be as accurate as your Arduino crystal allows. Date functions * will also rollover at midnight and year end.

I probably need to work over the date functions to make sure all the months function. I used the WWVB Simulator to check a few month endings, but if you don't check all iterations, you get bit by assuming it just works.

So, here it is, The Arduino - CMMR-6P-60 "Almost Accurate Clock"

/************************************************************************************************* * Arduino WWVB C-Max CMMR-6P-A2 clock v1.0 using the CMMR-6P-60 Eval Kit * * With apologies to Heathkit, we miss you! --> "The Almost Accurate Clock" * * Get the long tuned ferrite loopstick antenna and replace what's soldered to the board. * * Remote mount the receiver unit near a window if possible and well away from other electronic * equipment for best results. * * Continental US Summer longwave propagation will give best signal at sunrise and sunset. * Continental US Winter longwave propagation may give nearly 24 hr signal dependent on * your location and local interference. * * capt.tagon's contributions * * Decode WWVB data stream, Short impulse noise filtering, Drop bad frames, * Display: Signal Quality Indicator, Month & Day from Day of Year data, * Time Zone & DST Indicator for Local Time Display. Extend clock function * to increment day and year for midnight rollover and year end rollover. * * Choose UTC/Local Time, Choose between four US Time Zones for local time * * Note that UTC time display and Time Zones are set with DIP switches, flip settings * and hit reset button. Polling switches to dynamically change UTC/LocalTime/TZ messes * up the receiver decoding by introducing oddball delays that make it mistime the falling edge. * Dip-One 0=Local, Time 1=UTC | Dip-Two&Three 00=E, 01=C, 10=M, 11=P * * Frame Error Indicator - serves as signal indicator - Arduino serves as clock * during the noisy parts of the day, as it starts picking up WWVB frames, it syncs * to NIST time. * 1. Continuous full block indicates time as accurate as propagation delay will allow. * 2. Block and half block alternating. Signal is degrading, sync update when block shows. * 3. Half block, 5 or less sequential frame errors, low bar more than 5 frame errors. * 4. Low bar continuous, poor WWVB signal, no sync. Running on microprocessor clock only. * Your time will only be as accurate as your Arduino crystal allows. Date functions * will also rollover at midnight and year end. * * Contributed code that makes this all happen. Thank you for the informative websites, * time spent and sharing your code. I know how much I spent re-kajiggering for WWVB whose * data stream is a testament to cold war technology . * * Captain http://www.captain.at/electronic-atmega-dcf77.php * DCF77 time reception and decoding using the ATMega 16 * Mathias Dalheimer http://gonium.net/md/2006/11/05/arduino-dcf77-radio-clock-receiver/ * Interrupt driven clock and interrupt driven edge detection for DCF77 * Rudi Niemeijer http://www.rudiniemeijer.nl/wordpress/?p=516 * Amendments to DCF77 code, time display and addition of temperature functions * Peter H Anderson http://phanderson.com/lcd106/lcd107.html * LCD Display functions using LCD117 Serial LCD driver *************************************************************************************************/

/* Definitions for the timer interrupt 2 handler * The Arduino runs at 16 Mhz, we use a prescaler of 64 -> We need to * initialize the counter with 6. This way, we have 1000 interrupts per second. * We use tickCounter to count the interrupts. */#define INIT_TIMER_COUNT 6#define RESET_TIMER2 TCNT2 = INIT_TIMER_COUNTint tickCounter = 0;

/* WWVB time format struct - acts as an overlay on wwvbRxBuffer to extract time/date data. * All this points to a 64 bit buffer wwvbRxBuffer that the bits get inserted into as the * incoming data stream is decoded. */struct wwvbBuffer { unsigned long long U12 :4; // no value, empty four bits only 60 of 64 bits used unsigned long long Frame :2; // framing unsigned long long Dst :2; // dst flags unsigned long long Leapsec :1; // leapsecond unsigned long long Leapyear :1; // leapyear unsigned long long U11 :1; // no value unsigned long long YearOne :4; // year (5 -> 2005) unsigned long long U10 :1; // no value unsigned long long YearTen :4; // year (5 -> 2005) unsigned long long U09 :1; // no value unsigned long long OffVal :4; // offset value unsigned long long U08 :1; // no value unsigned long long OffSign :3; // offset sign unsigned long long U07 :2; // no value unsigned long long DayOne :4; // day ones unsigned long long U06 :1; // no value unsigned long long DayTen :4; // day tens unsigned long long U05 :1; // no value unsigned long long DayHun :2; // day hundreds unsigned long long U04 :3; // no value unsigned long long HourOne :4; // hours ones unsigned long long U03 :1; // no value unsigned long long HourTen :2; // hours tens unsigned long long U02 :3; // no value unsigned long long MinOne :4; // minutes ones unsigned long long U01 :1; // no value unsigned long long MinTen :3; // minutes tens};

/************************************************************************************************ * appendSignal() * * Append a decoded signal bit to the wwvbRxBuffer and decrement bufferPosition counter. * Argument can be 1 or 0. The bufferPosition counter shifts the writing position within * the buffer over 60 positions. Reverse bit order by starting at 63 and working back towards * 0 to account for MSB0/LSB0 mismach between processor and transmitted data. ************************************************************************************************/

/************************************************************************************************ * finalizeBuffer() * * Evaluate the information stored in the buffer. This is where the WWVB signal data is evaluated * and time/date values are decoded from BCD and the internal clock is updated. ************************************************************************************************/

/******************************************************************************************** * serialDumpTime() * * Dump the time, date and current temperature to the serial LCD ********************************************************************************************/

/* DCF77 sends month and day information, we aren't so lucky. * WWVB only sends Day of Year, Month and Day will need to be calculated, and 02/29 added for leapyear. * We're given Day of Year, compare against month ending day and calculate month and day. * Use leapyear flag to add one day to February. */ int eom = 0; // eom counter used to determine month and day while (eomYear[eom][lyr] < doy) { // calculate month and day for UTC day = doy - eomYear[eom][lyr]; mon = (eom + 1) % 12; eom++; }

// because of the local time offset the date changes early (Ohrs UTC). We need to calculate the // previous month and day to correct the local time date display. int peom = 0; // eom counter used to determine previous month and day while (eomYear[peom][lyr] < (doy - 1)) { // calculate previous month and day for local time offset prevday = (doy - 1) - eomYear[peom][lyr]; prevmon = (peom + 1) % 12; peom++; }

1 comment:

Interfacing with a DS1307 or DS1337 real time clock chip is pretty straightforward-- there are libraries posted to the Arduino forums if you'd like to get rid of the "almost" part...;)I'm using a DS1307 in my alarm clock project and would be happy to send my code if you're curious; photos here:http://www.flickr.com/photos/salsaviz/sets/72157617521587008/