Pages: 1 [2] 3 4   Go Down
Author Topic: DCF77 Time Module  (Read 7681 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 40
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It's near Stuttgart (South-west-Germany), so shouldn't be that much of a problem.
Code:
/**
 * 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.
« Last Edit: October 21, 2010, 12:58:57 pm by BajK » Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6250
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Why not try the code linked earlier in this thread?
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 40
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

London, England
Offline Offline
Edison Member
*
Karma: 4
Posts: 1026
Go! Go! Arduinoooo !!!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Newbie
*
Karma: 0
Posts: 40
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Did you manage it and how?
Logged

Berkshire, UK
Offline Offline
Full Member
***
Karma: 0
Posts: 120
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Chris

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

Berkshire, UK
Offline Offline
Full Member
***
Karma: 0
Posts: 120
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Chris

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

0
Offline Offline
Newbie
*
Karma: 0
Posts: 15
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 15
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 200
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Newbie
*
Karma: 0
Posts: 15
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Full Member
***
Karma: 0
Posts: 200
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

OK.  PON = ENABLE.

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

Thank you very much.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 15
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Full Member
***
Karma: 0
Posts: 200
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Thanks
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 15
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Pages: 1 [2] 3 4   Go Up
Jump to: