This piece of code is intended as the front end to an actual clock. It detects the pulse rising and falling edges, measures duration of the pulse, finds frame start, decodes mark, zero and one, provides for frame reject. The output at this point is the WWVB data stream, the BCD needs to be extracted for display and to update the clock.
The CMMR-6P-60 needs a little filtration on the power (10 uF and 0.1 uF capacitors), and I added an LED indicator to TCON to monitor activity (good vs bad signal). Hook the TCO data to your Arduino Data Input Pin 2.
Code: Select all/**********************************************************************
* Clock WWVB Data Decode Frame Reject by capt.tagon
*
* WWVB receiver input on digital pin 2
**********************************************************************/
#define wwvbIn 2 // WWVB receiver data input digital pin
#define ledRxPin 4 // WWVB receiver state indicator pin
#define ledFramePin 6 // Data received frame indicator pin
#define ledBitPin 5 // LED data decoded indicator pin
#define ledMarkPin 7 // Data received mark inicator pin
// variable changed by interrupt service routine - volatile
volatile byte wwvbInState; // store receiver signal level
byte prevWwvbInState; // store previous signal level
unsigned int prevEdgeMillis; // store time signal was read
byte bitVal; // bit decoded 0, 1, Mark or Frame
boolean badBit = 0; // bad bit, noise detected
byte markCount = 0; // mark count, 6 pulses per minute
boolean prevMark = 0; // store previous mark
byte bitCount = 0; // bits, 60 pulses per minute
boolean frameError = 0; // set for frame reject
unsigned int errorCount = 0; // keep count of frame errors
void setup() {
pinMode(wwvbIn, INPUT);
pinMode(ledRxPin, OUTPUT);
pinMode(ledFramePin, OUTPUT);
pinMode(ledBitPin, OUTPUT);
pinMode(ledMarkPin, OUTPUT);
attachInterrupt(0, readLevel, CHANGE); // fire interrupt on edge detected
Serial.begin(9600);
lcdInit();
}
void loop() {
if (wwvbInState != prevWwvbInState) {
pulseValue();
prevWwvbInState = wwvbInState;
}
}
/******************************************************************
* pulseValue()
*
* determine pulse width 200ms = 0, 500ms = 1, 800ms = mark
******************************************************************/
void pulseValue() {
unsigned int edgeMillis = millis(); // save current time
if (wwvbInState == 1) { // rising edge
prevEdgeMillis = edgeMillis; // set previous time to current
}
else { // falling edge
int pulseLength = edgeMillis - prevEdgeMillis; // calculate pulse length millis
badBit = 0; // clear bad bit detected
digitalWrite(ledMarkPin, LOW);
digitalWrite(ledFramePin, LOW);
if (pulseLength < 100) { // less than 100ms, noise pulses
badBit = 1; // bad bit, signal pulse noise
}
else if (pulseLength < 350) { // 800ms carrier drop mark
// two sequential marks -> start of frame. If we read 6 marks and 60 bits
// (0-59), we should have received a valid frame
if ((prevMark == 1) && (markCount == 6) && (bitCount == 59)) {
bitVal = 3;
frameAccept(); // data decoded, accept frame
digitalWrite(ledFramePin, HIGH); // frame received, ready for new frame
markCount = 0; // start counting marks, 6 per minute
prevMark = 0; // set bit counter to one
bitCount = 0; // should be a valid frame
frameError = 0; // set frame error indicator to zero
errorCount = 0; // set frame error count to zero
}
else if ((prevMark == 1) && ((markCount != 6) || (bitCount != 59))) {
errorCount ++; // increment frame error count
frameReject(); // bad decode, reject frame data
digitalWrite(ledFramePin, HIGH); // apparent frame, wrong mark and bit count
markCount = 0; // bad start of frame set mark count to zero
prevMark = 0; // clear previous to restart frame
bitCount = 0; // set bit count to one
frameError = 1; // and indicate frame error
}
else { // 10 second marker
bitVal = 2;
markCount ++; // increment mark counter, 6 per minute
digitalWrite(ledMarkPin, HIGH); // mark received
prevMark = 1; // set mark state to one, following mark indicates frame
bitCount ++; // increment bit counter
}
}
else if (pulseLength < 650) { // 500ms carrier drop one
bitVal = 1;
digitalWrite(ledBitPin, HIGH); // bit indicator LED on, one received
prevMark = 0; // set mark counter to zero
bitCount ++; // increment bit counter
}
else { // 200ms carrier drop zero
bitVal = 0;
digitalWrite(ledBitPin, LOW); // bit indicator LED off, zero received
prevMark = 0; // set mark counter to zero
bitCount ++; // increment bit counter
}
if (badBit == 0) { // reject noise
timeDateDecode();
}
}
}
/******************************************************************************
* readLevel() {
*
* Pin 2 INT0 Interrupt Handler Reads pin state - flashes signal indicator LED
******************************************************************************/
void readLevel() {
wwvbInState = digitalRead(wwvbIn); // read signal level
digitalWrite(ledRxPin, wwvbInState); // flash WWVB receiver indicator pin
}
/******************************************************************************
* timeDateDecode()
*
* Decode function to extract BCD from data stream
******************************************************************************/
void timeDateDecode() {
// Display bit values to terminal screen, output delimited data stream with
// colons at mark and new line at frame start. DEBUG ROUTINES
if (bitVal == 3) {
Serial.print("<- Frame Decode\n : ");
}
else if (bitVal == 2) {
Serial.print(" : ");
}
else {
Serial.print(bitVal, DEC);
}
}
/******************************************************************************
* frameAccept()
*
* Accept function for completed frame decode
******************************************************************************/
void frameAccept() {
// future BCD decode here
}
/******************************************************************************
* frameReject()
*
* Reject function for bad frame decode
******************************************************************************/
void frameReject() {
// Display Frame Reject message, mark count bit count and sequential number
// of frame errors. DEBUG ROUTINES
Serial.print("\n ->Scratch<- Bad Data - Marks: ");
Serial.print(markCount, DEC);
Serial.print(" Bits: ");
if (bitCount < 10) {
Serial.print("0");
}
Serial.print(bitCount, DEC);
Serial.print(" Frame Errors: ");
if (errorCount < 10) {
Serial.print("0");
}
Serial.println(errorCount, DEC);
}
/*****************************************************************************
* Time display functions
*****************************************************************************/
// LCD routines to initialize LCD and clear screen
void lcdInit() { // using P H Anderson Serial LCD driver board
Serial.print("?G216"); // configure driver for 2 x 16 LCD
delay(300);
Serial.print("?BDD"); // set backlight brightness
delay(300);
Serial.print("?f"); // clear screen
Serial.print("?c0"); // set cursor off
}
[/code]