DCF77 Time Module

Has anyone any experience of using the DCF77 radio time receiver module from Conrad? I have purchased one and have ran the example code from the tutorial in the playground. However, despite me putting the software into debugging mode and getting a report back that the Arduino is receiving data from the DCF77 the time never updates. The date and time stay at 00:00:00 all the time.

I have placed the receiver in different locations in case it was interference to no avail. I have a radio controlled clock on the wall of the same room that picks up a signal fine (no idea if it is the same signal though).

The code is however very old so maybe it is out of date? Has anyone tried to use a DCF77 with the Arduino and had any success? If so i'd like to hear from you please.

Thanks,

Mike

Mike, I posted source code for a library that decodes dcf77 signals in this thread: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1231336382

I don't use the conrad module (mine was pulled from a cheap radio clock) but it should work for you. Let me know how you get on.

Excellent. I will give that a go tonight. This should be up on the playground!!

Thanks Mem.

This should be up on the playground!!

yep - You would be welcome to add an article to the playground - or perhaps you can give me a hand with it- write down some notes of the kind of text you would like to see in the playground, post it here or send it to me via pm (click 'personal message' at the bottom of my post) and I will get it into a form for the playground.

Hi there,

please could you point me to the location in the playground where the article is?

Thx

Stefan

Stefan, it should be in the playground, but its not. Its waiting until I can find some spare time to write it up. Also I just got a UK MSF clock that I am hacking so I can test UK MSF code with that library. Perhaps when that is done I will do a writeup for the playground.

In the mean time, here is a link to a zip file with the current library and a simple sketch: Arduino Playground - HomePage

Mem where did you get the UK module if you don't mind me asking?

ebay - 5.99 shipped: http://cgi.ebay.co.uk/RADIO-CONTROLLED-LCD-ALARM-CLOCK-BY-KLICK-NEW-IN-BOX_W0QQitemZ260360732549QQcmdZViewItemQQptZUK_Clocks?hash=item260360732549&_trksid=p3911.c0.m14&_trkparms=72%3A1683|66%3A2|65%3A12|39%3A1|240%3A1318

Unlike the DCF module that I hacked out of another clock, this one has one big PCB that includes the LCD panel so its a little less flexible

edit: I just noticed that maplin have a MSF clock for 7.99, i have not taken one apart (yet) but you may want to have a look:
http://www.maplin.co.uk/Module.aspx?ModuleNo=225472

Is this DCF77 thing similar to the atomic clock radio signal that is used in continental North America? I don't know what electronics are required to receive those, but I see them all from a company called Oregon Scientific.

Yes halley, the signal from the US station is similar to the ones on Germany (DCF) and the UK (MSF).
There are very slight differences in the protocol but it should be easy to modify the code to cope. A google search for WWVB should yield more info on the protocol if you are interested

How are you getting on with this Mem ?

I got the clock (also got the one from Maplin as well) but found the MSF protocol was not as similar as I first thought. I would like to build a library to handle all three protocols (WWV, MSF and DCF) but its slow going due to pressure of work limiting my time.

I did not want to open a new thread. Yesterday I bought the DCF receiver at Conrad for like 10 Euro.

I wired everything together. Pin 1 to GND, Pin 2 to 5V and Pin 3 to the Arduino Pin 7 with a pull up resistor of 8,2 kOhm (did not have 8kOhm) as described in playground now.

If I run the program, the LCD shows some activity but the Serial dump is just alternating between 0:0:1 and 0:0:0 for time

Time: 0:0:1 Date: 0.0.0
Time: 0:0:0 Date: 0.0.0
Time: 0:0:1 Date: 0.0.0
Time: 0:0:0 Date: 0.0.0

Any ideas? I left it for half an hour and the only thing happened was that sometimes time went up to 0:0:4 but I don't get any reliable time?

Update: With the updates library it spits out a lot of debugging information.
Always saying Begin of a new minute every few seconds but seems to be appending someting and appending, so I will leave it along for some time.

can you post the code you are running

It's near Stuttgart (South-west-Germany), so shouldn't be that much of a problem.

/**
 * Arduino DCF77 decoder v0.2
 * Copyright (C) 2006 Mathias Dalheimer (md@gonium.net)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
 #include <MsTimer2.h>

/**
 * Where is the DCF receiver connected?
 */
#define DCF77PIN 2
/**
 * Where is the LED connected?
 */
#define BLINKPIN 13
/**
 * Turn debugging on or off
 */
#define DCF_DEBUG 1
/**
 * Number of milliseconds to elapse before we assume a "1",
 * if we receive a falling flank before - its a 0.
 */
#define DCF_split_millis 140
/**
 * There is no signal in second 59 - detect the beginning of 
 * a new minute.
 */
#define DCF_sync_millis 1200
/**
 * 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 tick_counter to count the interrupts.
 */
#define INIT_TIMER_COUNT 6
#define RESET_TIMER2 TCNT2 = INIT_TIMER_COUNT
int tick_counter = 0;
/**
 * DCF time format struct
 */
struct DCF77Buffer {
  unsigned long long prefix      :21;
  unsigned long long Min      :7;      // minutes
  unsigned long long P1            :1;      // parity minutes
  unsigned long long Hour      :6;      // hours
  unsigned long long P2            :1;      // parity hours
  unsigned long long Day      :6;      // day
  unsigned long long Weekday      :3;      // day of week
  unsigned long long Month      :5;      // month
  unsigned long long Year      :8;      // year (5 -> 2005)
  unsigned long long P3            :1;      // parity
};
struct {
      unsigned char parity_flag      :1;
      unsigned char parity_min            :1;
      unsigned char parity_hour      :1;
      unsigned char parity_date      :1;
} flags;
/**
 * Clock variables 
 */
volatile unsigned char DCFSignalState = 0;  
unsigned char previousSignalState;
int previousFlankTime;
int bufferPosition;
unsigned long long dcf_rx_buffer;
/**
 * time vars: the time is stored here!
 */
volatile unsigned char ss;
volatile unsigned char mm;
volatile unsigned char hh;
volatile unsigned char day;
volatile unsigned char mon;
volatile unsigned int year;
    

/**
 * used in main loop: detect a new second...
 */
unsigned char previousSecond;
    
/**
* Initialize the DCF77 routines: initialize the variables,
* configure the interrupt behaviour.
*/
void DCF77Init() {
previousSignalState=0;
previousFlankTime=0;
bufferPosition=0;
dcf_rx_buffer=0;
ss=mm=hh=day=mon=year=0;
#ifdef DCF_DEBUG
Serial.println("Initializing DCF77 routines");
Serial.print("Using DCF77 pin #");
Serial.println(DCF77PIN);
#endif
pinMode(BLINKPIN, OUTPUT);
pinMode(DCF77PIN, INPUT);

#ifdef DCF_DEBUG
Serial.println("Initializing timerinterrupt");
#endif
MsTimer2::set(1000, advanceClock); // 500ms period

#ifdef DCF_DEBUG
Serial.println("Initializing DCF77 signal listener interrupt");
#endif
attachInterrupt(0, int0handler, CHANGE);
}

/**
 * Append a signal to the dcf_rx_buffer. Argument can be 1 or 0. An internal
 * counter shifts the writing position within the buffer. If position > 59,
 * a new minute begins -> time to call finalizeBuffer().
 */
void appendSignal(unsigned char signal) {
#ifdef DCF_DEBUG
  Serial.print(", appending value ");
  Serial.print(signal, DEC);
  Serial.print(" at position ");
  Serial.println(bufferPosition);
#endif
  dcf_rx_buffer = dcf_rx_buffer | ((unsigned long long) signal << bufferPosition);
  // Update the parity bits. First: Reset when minute, hour or date starts.
  if (bufferPosition ==  21 || bufferPosition ==  29 || bufferPosition ==  36) {
      flags.parity_flag = 0;
  }
  // save the parity when the corresponding segment ends
  if (bufferPosition ==  28) {flags.parity_min = flags.parity_flag;};
  if (bufferPosition ==  35) {flags.parity_hour = flags.parity_flag;};
  if (bufferPosition ==  58) {flags.parity_date = flags.parity_flag;};
  // When we received a 1, toggle the parity flag
  if (signal == 1) {
    flags.parity_flag = flags.parity_flag ^ 1;
  }
  bufferPosition++;
  if (bufferPosition > 59) {
    finalizeBuffer();
  }
}

/**
 * Evaluates the information stored in the buffer. This is where the DCF77
 * signal is decoded and the internal clock is updated.
 */
void finalizeBuffer(void) {
  if (bufferPosition == 59) {
#ifdef DCF_DEBUG
    Serial.println("Finalizing Buffer");
#endif
    struct DCF77Buffer *rx_buffer;
    rx_buffer = (struct DCF77Buffer *)(unsigned long long)&dcf_rx_buffer;
    if (flags.parity_min == rx_buffer->P1  &&
        flags.parity_hour == rx_buffer->P2  &&
        flags.parity_date == rx_buffer->P3) 
    { 
#ifdef DCF_DEBUG
      Serial.println("Parity check OK - updating time.");
#endif
      //convert the received bits from BCD
      mm = rx_buffer->Min-((rx_buffer->Min/16)*6);
      hh = rx_buffer->Hour-((rx_buffer->Hour/16)*6);
      day= rx_buffer->Day-((rx_buffer->Day/16)*6); 
      mon= rx_buffer->Month-((rx_buffer->Month/16)*6);
      year= 2000 + rx_buffer->Year-((rx_buffer->Year/16)*6);
    }
#ifdef DCF_DEBUG
      else {
        Serial.println("Parity check NOK - running on internal clock.");
    }
#endif
  } 
  // reset stuff
  ss = 0;
  bufferPosition = 0;
  dcf_rx_buffer=0;
}

/**
 * Dump the time to the serial line.
 */
void serialDumpTime(void){
  Serial.print("Time: ");
  Serial.print(hh, DEC);
  Serial.print(":");
  Serial.print(mm, DEC);
  Serial.print(":");
  Serial.print(ss, DEC);
  Serial.print(" Date: ");
  Serial.print(day, DEC);
  Serial.print(".");
  Serial.print(mon, DEC);
  Serial.print(".");
  Serial.println(year, DEC);
}


/**
 * Evaluates the signal as it is received. Decides whether we received
 * a "1" or a "0" based on the 
 */
void scanSignal(void){ 
    if (DCFSignalState == 1) {
      int thisFlankTime=millis();
      if (thisFlankTime - previousFlankTime > DCF_sync_millis) {
#ifdef DCF_DEBUG
        Serial.println("####");
        Serial.println("#### Begin of new Minute!!!");
        Serial.println("####");
#endif
        finalizeBuffer();
      }
      previousFlankTime=thisFlankTime;
#ifdef DCF_DEBUG
      Serial.print(previousFlankTime);
      Serial.print(": DCF77 Signal detected, ");
#endif
    } 
    else {
      /* or a falling flank */
      int difference=millis() - previousFlankTime;
#ifdef DCF_DEBUG
      Serial.print("duration: ");
      Serial.print(difference);
#endif
      if (difference < DCF_split_millis) {
        appendSignal(0);
      } 
      else {
        appendSignal(1);
      }
    }
}

/**
* The interrupt routine for counting seconds – increment hh:mm:ss.
*/
void advanceClock() {
ss++;
if (ss==60) {
ss=0;
mm++;
if (mm==60) {
mm=0;
hh++;
if (hh==24)
hh=0;
}
}
}

/**
* Interrupthandler for INT0 – called when the signal on Pin 2 changes.
*/
void int0handler() {
int length = millis() - previousFlankTime;
if (length < 10) {
// ignore jitter
return;
}
// check the value again – since it takes some time to
// activate the interrupt routine, we get a clear signal.
DCFSignalState = digitalRead(DCF77PIN);
}


/**
 * Standard Arduino methods below.
 */

void setup(void) {
  // We need to start serial here again, 
  // for Arduino 007 (new serial code)
  Serial.begin(9600);
  DCF77Init();
}

void loop(void) {
  if (ss != previousSecond) {
    serialDumpTime();
    previousSecond = ss;
  }
  if (DCFSignalState != previousSignalState) {
    scanSignal();
    if (DCFSignalState) {
      digitalWrite(BLINKPIN, HIGH);
    } else {
      digitalWrite(BLINKPIN, LOW);
    }
    previousSignalState = DCFSignalState;
  }
    //delay(20);
}

It's that example from gonium.net » Blog Archive » Arduino DCF77 v0.2 released (with some changes that someone in the comments made, using Mstimer2 instead)

Is there a way how to know whether it is just not working or I am out of range? Because atm I cannot go outside with my Arduino. I first have to wire together my lcd and box which requires me for the light to be attached as well. So I can at soonest by Saturday test it outside.

Why not try the code linked earlier in this thread?

Because it's a different receiver and the pin logic is totally different in the first post. I tried the DCF77 tutorial in playground as well which ecplicitly states it uses the DCF receiver of Conrad.

I never did get my Conrad receiver to work with this.

Did you manage it and how?