DCF77 Time Module

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?

Has anyone got this working with a UK MSF signal yet?
I'm looking into building a fairly large LED clock that must be radio controlled. I wouldn't mind using an Arduino for that project but it does look complicated.

Oh, actually, never mind. I just found a far easier way of getting atomic time on the Arduino. Using a GPS module. ;D

It has a serial interface that just sends a string with UTC time and current coordinates. And a cheap one costs about the same as a MSF receiver.

I suspect GPS may not work very well indoors, unless you get a really sensitive receiver or use an external antenna.

I've just ordered an MSF module (£7 from PV Electronics, also sold on eBay by yashu75) :

They also do DCF and WWVB modules. And Sparkfun do a similar (not identical) module for WWVB, as well:

My plan is to extend/modify RadioTime to do MSF instead of DCF. MSF docs are here:

Mem - you said you had made a start on the MSF code - do you have any bits to share, or should I just start in ? Or, does anyone else have MSF code running ?

Ta!

Richard

I've got MSF code running happily now against one of those modules.

Details and code are here:

http://www.jarkman.co.uk/catalog/robots/msftime.htm

Hello.

I'm very interested in the DCF77 Time Module and I have order one from PVElectronics, but I have some questions about its technical data:

Supply voltage range: Max 5V, Min 1.8.
What voltage are you using? 3.3 or 5v?

PON Input Level High: Min 0.85.
Is this enough to arduino? is some interface transistor needed?

Thank you very much.

I have powered mine from the 3.3v rail on a Uno, and I have not had any interface issues. I just wired the output pin of the module directly to analogue 0 on the Arduino. I'm using the pin as a digital input, not an analogue one.

I have wired the PON pin to ground, which keeps the receiver powered up all the time, so I am not interested in the PON levels.

OK. PON = ENABLE.

Is there any special reason to use analog instead of digital?

Thank you very much.

Yes, I am using it with a LOLShield and it ties up all the digital pins. Analogue is all that was left! :slight_smile:

Jarkman, are you using any pull-up resistor like:
http://gonium.net/md/2006/11/05/arduino-dcf77-radio-clock-receiver/
?

Thanks

No, I found that my receiver did not require one.

Incidentally, I have found that my receiver's reliability varies with the time of day. It generally gets a fix first time in the evening (which means it knows the time within 120 seconds of boot), but during the daytime it can take 15 minutes.

I think the difference is down to radio propagation changes with time of day. I intent to rework some of my code to make it less vulnerable to short spikes, which ought to help.

That's the reason why I will use a combination of a ds1307 and a dcf77 receiver.
DS1307 to keep time and dcf77 to make corrections.

The dcf77 module will sync the RTC every start of minute found.