Go Down

Topic: DCF77 Time Module (Read 10825 times) previous topic - next topic

BajK

#15
Oct 21, 2010, 07:55 pm Last Edit: Oct 21, 2010, 07:58 pm by BajK Reason: 1
It's near Stuttgart (South-west-Germany), so shouldn't be that much of a problem.
Code: [Select]
/**
* 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 http://gonium.net/md/2007/01/06/arduino-dcf77-v02-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.

mem

Why not try the code linked earlier in this thread?

BajK

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.

Mike Mc

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

BajK

Did you manage it and how?

yesyes

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.
Chris

Location: Berkshire, UK
My Astro and DIY projects website: http://yesyes.info/

yesyes

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.
Chris

Location: Berkshire, UK
My Astro and DIY projects website: http://yesyes.info/

Jarkman

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) :
http://www.pvelectronics.co.uk/index.php?main_page=index&cPath=9

They also do DCF and WWVB modules. And Sparkfun do a similar (not identical) module for WWVB, as well:
http://www.sparkfun.com/products/10060


My plan is to extend/modify RadioTime to do MSF instead of DCF. MSF docs are here:
http://www.pvelectronics.co.uk/rftime/msf/MSF_Time_Date_Code.pdf


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


Jarkman

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


Jose Francisco

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.

Jarkman

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.

Jose Francisco

OK.  PON = ENABLE.

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

Thank you very much.

Jarkman

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

Jose Francisco

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

Thanks

Jarkman

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.

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy