Prpgramm zum Auswerten des DCF Signals

Hallo zusammen,
Ich habe schon viel rum experimentiert bekomme es aber leider nicht gebacken. Und zwar bräuchte ich für den Arduino ein Programm welches beim Start des Programms einmal das DCF-77 Signal auswertet. Das Signal sollte an Pin 2 der digitalen Ports eingespeist werden. Die dabei entstehende Uhrzeit sollte als Variable eingetragen werden, z.B. es ist 9:57 dann sollte die erste Variable z.B. x = 9 sein und die zweite Variable z.B. y 57 sein. Kann mir da jemand weiterhelfen? Würde auch was bezahlen dafür. Hoffentlich findet sich jemand der mir dort helfen kann. Schon mal Vielen Dank im Voraus.

Mit freundlichen Grüßen
heni2311

Gibt es fix und fertig hier Arduino Playground - DCF77

Danke für die Antwort, leider habe ich vom Programmieren nicht all zu viel Ahnung, ich verstehe da meistens nur Bahnhof. So einfache Sachen wie LED's ansteuern ist alles kein Problem aber für sowas programmiere ich noch nicht lang genug dafür. Sie soll sich ja nur einmal automatisch einstellen beim Start des Programmes. Könnten Sie mir das vielleicht ein bisschen erkären?

heni2311:
Ich habe schon viel rum experimentiert bekomme es aber leider nicht gebacken. Und zwar bräuchte ich für den Arduino ein Programm welches beim Start des Programms einmal das DCF-77 Signal auswertet.

Dass dieses "einmal auswerten" vom Prinzip her (DCF Datenrate 1 Baud = in einer Minute überträgt das DCF-Protokoll genau eine Zeitangabe), allerdings mindestens(!) 1-2 Minuten dauert, und bei schlechten Empfangsbedingungen auch wesentlich länger (viele Minuten, Stunden, Tage etc.) dauern kann oder bei sehr schlechten Bedingungen (elektromagnetische Störungen durch Netzteile, Energiesparlampen, Monitore, Stromleitungen etc.) sogar vollkommen unmöglich sein kann, ist Dir dabei aber schon klar?

Hast Du denn bereits irgendeinen DCF77-Test-Sketch mit Deinem DCF-Modul und unter Deinen Empfangsbedingungen zum Laufen bekommen?

Ja habe ich. Dort wurde das Signal auch direkt beim ersten Mal richtig erkannt. Ich habe mir mal ein Testprogramm runtergeladen welches den Anfang einer Minute findet und auch den Rest im Prinzip auswertet, leider nur auf den seriellen Monitor im Arduino Programm selbst. Es erkennt ob ein Impuls High oder Low ist und um welche Bit es sich handelt. Allerdings schaffe ich es nicht das vernünftig umzuwandeln so das die Zeit halt als variable angegeben wird.

heni2311:
Ja habe ich. Dort wurde das Signal auch direkt beim ersten Mal richtig erkannt. Ich habe mir mal ein Testprogramm runtergeladen welches den Anfang einer Minute findet und auch den Rest im Prinzip auswertet, leider nur auf den seriellen Monitor im Arduino Programm selbst. Es erkennt ob ein Impuls High oder Low ist und um welche Bit es sich handelt. Allerdings schaffe ich es nicht das vernünftig umzuwandeln so das die Zeit halt als variable angegeben wird.

Was Du da schreibst, verstehe ich nicht.

Wenn Du ein Programm hast, das in der Lage ist, die Zeit auf dem seriellen Monitor auszugeben, dann hat das Programm die Zeit auch in einer Variablen decodiert. Dann brauchst Du Dir - wenn eine andere Verwendung geplant ist - diese Zeit nur zu merken. Entweder zusätzlich zur Ausgabe auf Serial, oder anstelle.

Möchtest Du das Programm mal hier posten?
Oder soll ich Dir mal ein anderes Programm hier posten?

Ich steuere Siebensegmentanzeigen damit an, welche an einen Zähler angeschlossen sind. Der Zähler benötigt also nur einen Takt um eins hoch zu zählen. Das bedeutet wenn es jetzt z.B. 16:45Uhr ist muss der erste Zähler für die Minuten 45 mal getaktet werden und der zweite 16 mal, für die Stunden. Dies wollte ich mit einer for loop realisieren. Dafür brauche ich dann zwei variablen um die zwei Zähler anzusteuern.

Das ist das Programm zum finden des Minuten Anfangs. Wenn du möchtest kannst du dein Programm auch gerne mal posten.

#define BLINKPIN 13
#define DCF77PIN 2

int SignalHIGHStart = 0;
int SignalHIGHEnde = 0;
int SignalHIGHZeit = 0;
int SignalLOWStart = 0;
int SignalLOWEnde = 0;
int SignalLOWZeit = 0;

bool Signal = false;
bool neueMinute = false;
int bitnr = -1;

void setup() {
Serial.begin(9600);
pinMode(DCF77PIN, INPUT);
pinMode(BLINKPIN, OUTPUT);
Serial.println("Warte auf Start einer neuen Minute...");

}

void loop() {
int pinValue = digitalRead(DCF77PIN); //Wert am PIN einlesen

if (pinValue == HIGH && Signal == false) { //PIN ist HIGH, vorher war kein HIGH
Signal = true;
SignalHIGHStart = millis();
SignalLOWEnde = SignalHIGHStart;
SignalLOWZeit = SignalLOWEnde - SignalLOWStart;

//DEBUG Ausgabe nach Serial
if (neueMinute) {PrintBeschreibung(bitnr);
Serial.print("BitNr.:"); Serial.print (bitnr);
Serial.print (" Wert :"); Serial.print (werteBitAus(SignalHIGHZeit));
Serial.println (" ");
}
} //ENDE (pinValue == HIGH && Signal == false)

if (pinValue == LOW && Signal == true) { //PIN ist LOW vorher war HIGH
Signal = false;
SignalHIGHEnde = millis();
SignalLOWStart = SignalHIGHEnde;
SignalHIGHZeit = SignalHIGHEnde - SignalHIGHStart;

feststellenObNeueMinute(SignalLOWZeit);
} //ENDE (pinValue == LOW && Signal == true)

} //END LOOP

//Gebe den Wert zurück den das Bit aufgrund der HIGH Zeit-Länge haben muss
int werteBitAus (int SignalHIGHZeit) {
//mit den Zeiten ein wenig größzügig sein, die schwanken um den Optimalwert
if (SignalHIGHZeit >= 90 && SignalHIGHZeit <= 110) {return 0;}
if (SignalHIGHZeit >= 190 && SignalHIGHZeit <= 210) {return 1;}
}

//Wenn LOW Zeit größer 1750ms dann neue Minute BitNummer auf 0 setzen,
//ansonsten BitNummer hochzählen
void feststellenObNeueMinute (int SignalLOWZeit) {
//auch hier ein wenig Luft lassen bei der Zeit.
if (SignalLOWZeit >= 1750) {bitnr = 0; neueMinute = true;} else {bitnr++; }
}

void PrintBeschreibung(int BitNummer) {
switch (BitNummer) {
case 0: Serial.println("\n### S T A R T N E U E M I N U T E"); break;
case 1: Serial.println("\n### CODIERTE WETTERDATEN"); break;
case 15: Serial.println("\n### RUFBIT RESERVEANTENNE"); break;
case 16: Serial.println("\n### ANKUENDIGUNG UMSTELLUNG MEZ/MESZ"); break;
case 17: Serial.println("\n### 0 = MEZ | 1 = MESZ"); break;
case 18: Serial.println("\n### 0 = MESZ | 1 = MEZ"); break;
case 19: Serial.println("\n### 1 = SCHALTSEKUNDE AM ENDE DER STUNDE"); break;
case 20: Serial.println("\n### BEGIN ZEITINFORMATION (IMMER 1)"); break;
case 21: Serial.println("\n### BEGIN MINUTEN"); break;
case 28: Serial.println("\n### PARITAET MINUTE"); break;
case 29: Serial.println("\n### BEGIN STUNDE");break;
case 35: Serial.println("\n### PARITAET STUNDE"); break;
case 36: Serial.println("\n### BEGIN KALENDERTAG"); break;
case 42: Serial.println("\n### BEGIN WOCHENTAG"); break;
case 45: Serial.println("\n### BEGIN MONATSNUMMER"); break;
case 50: Serial.println("\n### BEGIN JAHR"); break;
case 58: Serial.println("\n### PARITAET DATUM"); break;
}

}

heni2311:
Das ist das Programm zum finden des Minuten Anfangs. Wenn du möchtest kannst du dein Programm auch gerne mal posten.

Code bitte immer innerhalb von Code-Tags posten! Siehe "How to use this forum"!

Hierst ist mal was, das ich gemacht habe:

#define DCFPIN 8
#define INVERTEDSIGNAL false

#define PULSE_ERRORLIMIT 40  // 40 ms seems to be a suitable value
#define PULSE_SHORTSECOND (1000-PULSE_ERRORLIMIT)
#define PULSE_LONGSECOND  (1000+PULSE_ERRORLIMIT)
#define PULSE_MINUTEMARK  (2000-PULSE_ERRORLIMIT)

// 8 Bytes = 64 Bits to store all 59 DCF bits in a minute
uint8_t dcfBits[8];  
byte dcfBitcount;

boolean parityOK(byte startbit, byte paritybit)
{ // test parity bit in the range startbit ... paritybit-1
  byte p=0;
  for (int i=startbit; i<=paritybit;i++) p+= bitRead(dcfBits[i/8],i%8);
  if (p%2 == 0) return true; // even parity detected
  else return false;
}

byte dcfDecodeBCD(byte startbit, byte numbits)
{ // return BCD encoded byte-value beginning at startbit
  byte b=0;
  for (int i=startbit;i<startbit+numbits;i++) bitWrite(b,i-startbit, bitRead(dcfBits[i/8],i%8));
  return b;
}

boolean dcfDecodeTime()
{
  int hour, minute, day, month, year;
  byte resultCode, parityError=0;
  // print bits of time code to serial
  for (int i=0;i<dcfBitcount;i++) Serial.print(bitRead(dcfBits[i/8],i%8));
  Serial.print('\t');Serial.println(dcfBitcount);
  if(!parityOK(21,28)) bitSet(parityError,0); // parity error minute
  if(!parityOK(29,35)) bitSet(parityError,1); // parity error hour
  if(!parityOK(36,58)) bitSet(parityError,2); // parity error date
  hour   = 10*dcfDecodeBCD(33,2);  // tens of hours, 2 bit 
  hour  += dcfDecodeBCD(29,4);     // single hours, 4 bit 
  minute = 10*dcfDecodeBCD(25,3);  // tens of minutes, 3 bit
  minute += dcfDecodeBCD(21,4);    // single minutes, 4 bit

  day = 10*dcfDecodeBCD(40,2);     // day of month (tens), 2 bit
  day += dcfDecodeBCD(36,4);       // day of month (single), 4 bit
  month = 10*dcfDecodeBCD(49,1);   // tens of month, 1 bit
  month += dcfDecodeBCD(45,4);     // single digit of month, 4 bit
  year = 10*dcfDecodeBCD(54,4);    // tens of year, 4 bit
  year += dcfDecodeBCD(50,4);      // single digit of year, 4 bit
  if (dcfBitcount!=59)
  {
    Serial.println("Error: Time code must contain 59 bits!");
  }
  else if (parityError)
  {
    Serial.println("Error: Wrong parity bit detected!");
  }
  else
  {
    Serial.print(hour);Serial.print(':');
    Serial.print(minute);Serial.print(":00  ");
    Serial.print(day);Serial.print('/');
    Serial.print(month);Serial.print('/');
    Serial.println(year);
    dcfBitcount=0;
    return true;
  }
  dcfBitcount=0;
  return false;
}


boolean dcfHandleBit(uint16_t hiTime, uint16_t pulseTime)
{
  if (dcfBitcount<60)
  {
    if (hiTime<150) bitClear(dcfBits[dcfBitcount/8],dcfBitcount%8);
    else  bitSet(dcfBits[dcfBitcount/8],dcfBitcount%8);
    dcfBitcount++;  
  }
  if (pulseTime>=PULSE_MINUTEMARK) return dcfDecodeTime();
  return false;
}

boolean dcfUpdate()
{
  static boolean lastState;
  static unsigned long lastChangeTime;
  static uint16_t hiMillis,loMillis;
  static byte cnt;
  boolean state= digitalRead(DCFPIN);
  if (INVERTEDSIGNAL) state=!state;
  if (state!=lastState)
  {
    long timeDiff=millis()-lastChangeTime;
    lastState=state;
    lastChangeTime+= timeDiff;
    if (state)
    {
      cnt++;
      loMillis+= timeDiff;
      uint16_t pulsetime=hiMillis+loMillis;
      if ((pulsetime>=PULSE_SHORTSECOND && pulsetime<=PULSE_LONGSECOND)|| pulsetime>=PULSE_MINUTEMARK) 
      {
        Serial.print(cnt);Serial.print('\t');
        Serial.print(hiMillis);Serial.print('\t');
        Serial.print(loMillis);Serial.print('\t');
        Serial.print(pulsetime);Serial.print('\t');
        Serial.println();
        boolean result=dcfHandleBit(hiMillis, pulsetime);
        hiMillis=0;
        loMillis=0;
        cnt=0;
        return result;
      }
    }
    else hiMillis+= timeDiff;
  }
  return false;
}




void setup() {
  Serial.begin(9600);
  Serial.println("DCF77 test sketch by 'jurs'");
  pinMode(DCFPIN, INPUT);
}

void loop() {
  if (dcfUpdate()) Serial.println("SUCCESS!");
}

Ok, danke ich wusste nicht das es solche Code Schlagwörter gibt hab ihn jetzt nochmal in einem solchen Schlagwort. Könntest du das mit den Variablen für die Anzahl an Wiederholungen der for Schleife auch in mein Programm einfügen und mir dann sagen welche Variable für Stunde und Minute zuständig ist. Wäre sehr nett von dir.

#define BLINKPIN 13
#define DCF77PIN 2
 
int SignalHIGHStart = 0;
int SignalHIGHEnde = 0;
int SignalHIGHZeit = 0;
int SignalLOWStart = 0;
int SignalLOWEnde = 0;
int SignalLOWZeit = 0;
 
bool Signal = false;
bool neueMinute = false;
int bitnr = -1;

void setup() {
  Serial.begin(9600);
  pinMode(DCF77PIN, INPUT);
  pinMode(BLINKPIN, OUTPUT);
  Serial.println("Warte auf Start einer neuen Minute...");

}

void loop() {
  int pinValue = digitalRead(DCF77PIN); //Wert am PIN einlesen
 
  if (pinValue == HIGH && Signal == false) { //PIN ist HIGH, vorher war kein HIGH
    Signal = true; 
    SignalHIGHStart = millis(); 
    SignalLOWEnde = SignalHIGHStart;  
    SignalLOWZeit = SignalLOWEnde - SignalLOWStart; 
 
    //DEBUG Ausgabe nach Serial
    if (neueMinute) {PrintBeschreibung(bitnr);
      Serial.print("BitNr.:"); Serial.print (bitnr); 
      Serial.print (" Wert :"); Serial.print (werteBitAus(SignalHIGHZeit)); 
      Serial.println (" ");
    }
  } //ENDE (pinValue == HIGH && Signal == false)
 
  if (pinValue == LOW && Signal == true) { //PIN ist LOW vorher war HIGH
    Signal = false; 
    SignalHIGHEnde = millis();  
    SignalLOWStart = SignalHIGHEnde; 
    SignalHIGHZeit = SignalHIGHEnde - SignalHIGHStart; 
 
    feststellenObNeueMinute(SignalLOWZeit);
  } //ENDE (pinValue == LOW && Signal == true)
 
} //END LOOP
 
//Gebe den Wert zurück den das Bit aufgrund der HIGH Zeit-Länge haben muss 
int werteBitAus (int SignalHIGHZeit) {
  //mit den Zeiten ein wenig größzügig sein, die schwanken um den Optimalwert
   if (SignalHIGHZeit >= 90 && SignalHIGHZeit <= 110) {return 0;} 
   if (SignalHIGHZeit >= 190 && SignalHIGHZeit <= 210) {return 1;}
}
 
//Wenn LOW Zeit größer 1750ms dann neue Minute BitNummer auf 0 setzen, 
//ansonsten BitNummer hochzählen
void feststellenObNeueMinute (int SignalLOWZeit) {
  //auch hier ein wenig Luft lassen bei der Zeit.
  if (SignalLOWZeit >= 1750) {bitnr = 0; neueMinute = true;} else {bitnr++; }
}
 
void PrintBeschreibung(int BitNummer) {
  switch (BitNummer) {
    case  0: Serial.println("\n### S T A R T  N E U E  M I N U T E"); break;
    case  1: Serial.println("\n### CODIERTE WETTERDATEN"); break;
    case 15: Serial.println("\n### RUFBIT RESERVEANTENNE"); break;
    case 16: Serial.println("\n### ANKUENDIGUNG UMSTELLUNG MEZ/MESZ"); break;
    case 17: Serial.println("\n### 0 = MEZ | 1 = MESZ"); break;
    case 18: Serial.println("\n### 0 = MESZ | 1 = MEZ"); break;
    case 19: Serial.println("\n### 1 = SCHALTSEKUNDE AM ENDE DER STUNDE"); break;
    case 20: Serial.println("\n### BEGIN ZEITINFORMATION (IMMER 1)"); break;
    case 21: Serial.println("\n### BEGIN MINUTEN"); break;
    case 28: Serial.println("\n### PARITAET MINUTE"); break;
    case 29: Serial.println("\n### BEGIN STUNDE");break;
    case 35: Serial.println("\n### PARITAET STUNDE"); break;
    case 36: Serial.println("\n### BEGIN KALENDERTAG"); break;
    case 42: Serial.println("\n### BEGIN WOCHENTAG"); break;
    case 45: Serial.println("\n### BEGIN MONATSNUMMER"); break;
    case 50: Serial.println("\n### BEGIN JAHR"); break;
    case 58: Serial.println("\n### PARITAET DATUM"); break;
  }

heni2311:
Könntest du das mit den Variablen für die Anzahl an Wiederholungen der for Schleife auch in mein Programm einfügen

Wie meinen? Welche for-Schleife?

Mein Programm enthält mehrere for-Schleifen und alle diese for-Schleifen werden bei der Decodierung der empfangenen Sekunden-Bits eingesetzt.

Um in Deinem Programm aber irgendwas zu dekodieren oder weiter zu verarbeiten, sobald die Bits in einer Minute voll sind, müßtest Du erstmal anfangen, die empfangen Bits (0 oder 1) erstmal irgendwo zu speichern.

Du speicherst in Deinem Programm bisher nur einen Bit-Zähler "bitnr", aber Du speicherst die empfangenen Bits nicht.

In meinem Programm werden die 59 Sekunden-Bits in einem 8 Byte großen Array weggespeichert:

// 8 Bytes = 64 Bits to store all 59 DCF bits in a minute
uint8_t dcfBits[8];

Das ist äußerst speichersparend. Da jedes Byte 8 Bits speichern kann, könnte ich darin bis zu 64 Bits speichern, aber gebraucht werden ja nur 59 davon. Jedesmal, wenn ein Minutenumpuls empfangen wurde, wird der Bitzähler (in meinem Programm "dcfBitcount") auf Null zurückgesetzt, und beim Empfang eines Sekundenimpulses wird das empfangene Bit (0 oder 1) weggespeichert. Wenn 59 Bits empfangen wurden, wird ausgewertet.

Schau dir mal das an...

/*
 * InternalClockSync.pde
 * example code illustrating time synced from a DCF77 receiver
 * Thijs Elenbaas, 2012
 * This example code is in the public domain.
 
  This example shows how to fetch a DCF77 time and synchronize
  the internal clock. In order for this example to give clear output,
  make sure that you disable logging  from the DCF library. You can 
  do this by commenting out   #define VERBOSE_DEBUG 1   in Utils.cpp. 
 */

#include "DCF77.h"
#include "Time.h"

#define DCF_PIN 2	         // Connection pin to DCF 77 device
#define DCF_INTERRUPT 0		 // Interrupt number associated with pin

time_t time;
DCF77 DCF = DCF77(DCF_PIN,DCF_INTERRUPT);


void setup() {
  Serial.begin(9600); 
  DCF.Start();
  Serial.println("Waiting for DCF77 time ... ");
  Serial.println("It will take at least 2 minutes until a first update can be processed.");
}

void loop() {
  delay(1000);
  time_t DCFtime = DCF.getTime(); // Check if new DCF77 time is available
  if (DCFtime!=0)
  {
    Serial.println("Time is updated");
    setTime(DCFtime);
  }	
  digitalClockDisplay();  
}

void digitalClockDisplay(){
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year()); 
  Serial.println(); 
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

Der Code startet dir die dcf Abfrage und sagt deiner arduino uhr bei jeder erfolgreichen Abfrage die neue Zeit...somit hast du je nach Empfangsqualität deine Uhrzeit jederzeit auf die Sekunde genau.

die einzelnen Werte stecken in

hour() minute() second() day() month() year() und noch weitere...