Alles hat eine genaue Zeit - DCF77 mit dem Arduino - AZ-Delivery

Nous avons déjà construit des horloges, soit le temps a été calculé sur la base des entrées manuelles et des temps d'attente, par NTP ou avec un module RTC. Aujourd'hui, nous aimerions vous présenter une nouvelle façon de "gagner du temps" et cela via DCF77.

L'émetteur DCF77:

À Mainflingen, près de Francfort-sur-le-Main, il existe un émetteur à ondes longues pour 77,5 kHz. Avec 50 000 W, cet émetteur envoie l'heure actuelle codée une fois par minute.

Cet émetteur et le signal est appelé DCF77. La désignation DCF77 est l'indicatif d'appel attribué à l'émetteur pour l'identification internationale.

Avec un module de réception correspondant (par ex. pollin.de 810054), le signal peut être reçu et décodé dans un rayon allant jusqu'à 2000 km autour de Mainflingen.

Pour information, d'autres émetteurs de signaux horaires sont utilisés dans d'autres pays, avec des fréquences différentes:

Indicatif Emplacement Fréquences
Bêta La russie 25 kHz
BPC La Chine 68,5 kHz
BPL La Chine 100 kHz
BPM Chine, Lintong (Xi’an) 2,5 MHz, 5 MHz, 10 MHz, 15 MHz
CHU Canada, Ottawa 3,330 MHz, 7,850 MHz, 14,670 MHz
DCF77 Allemagne, Mainflingen 77,5 kHz
IBF Italie, Turin 5 MHz
JJY Japon, Mont Ōtakadoya 40 kHz
JJY Japon, Hagane Mountain 60 kHz
MIKES Finlande, Espoo 25 MHz
MSF Grande-Bretagne, Anthorn 60 kHz
RBU Russie, Taldom 66,66 kHz
RTZ Russie, Irkutsk 50 kHz
RWM Russie, Moscou 4,996 MHz, 9,996 MHz, 14,996 MHz
TDF France, Allouis 162 kHz
WWV États-Unis, Fort Collins 2,5 MHz, 5 MHz, 10 MHz, 15 MHz, 20 MHz
WWVB États-Unis, Fort Collins 60 kHz
WWVH États-Unis, Hawaï, Kekaha 2,5 MHz, 5 MHz, 10 MHz, 15 MHz
YVTO Venezuela, Caracas 5 MHz

 

Structure du signal DCF77:

L'émetteur DCF77 envoie plusieurs informations en 60 minutes.

Les informations sont codées en impulsions d'une seconde. Une impulsion élevée de 100 ms (900 ms FAIBLE) signifie un 0 et une impulsion élevée de 200 ms (800 ms FAIBLE) un 1. Ce codage "binaire" peut ensuite être calculé en date et heure, plus CEST / CET et les données météorologiques.

59 bits sont transmis, avec la signification suivante:

Bit Signification Vaut
0 Démarrer une nouvelle minute (toujours 0)
1 crypté
 Données météo
2
3
4
5
6
7
8
9
10
11
12
13
14
15 Bit d'appel
16 Passage CET / CEST
17 CEST
18 CET
19 Leap second
20 Informations sur l'heure de début (toujours 1)
21 Minute 1
22 2
23 4
24 8
25 10
26 20
27 40
28 Minute de parité
29 Heure 1
30 2
31 4
32 8
33 10
34 20
35 Heure de parité
36 Date jour 1
37 2
38 4
39 8
40 10
41 20
42 Jour de semaine 1
43 2
44 4
45 Date mois 1
46 2
47 4
48 8
49 10
50 Date année 1
51 2
52 4
53 8
54 10
55 20
56 40
57 80
58 Date de parité

 

Sur la base de ces informations, nous pouvons créer un croquis Arduino:

Nous connectons notre récepteur DCF77 à la broche 2 de l'Arduino.

Croquis Arduino:

#define STATUS_PIN LED_BUILTIN
#define DCF_PIN 2
 
int HIGH_Start = 0;
int HIGH_end = 0;
int HIGH_time = 0;
int LOW_Start = 0;
int LOW_end = 0;
int LOW_time = 0;
 
bool Signal = faux;
bool nouvelle minute = faux;
int BIT = -1;
int TIME[65];
int TIME_ HOUR;
int TIME_MINUTE;
int TIME_DAY;
int TIME_MONTH;
int TIME_YEAR;
int TIME_WEEKDAY;
int PAR_ HOUR;
int PAR_MINUTE;
int PAR_BEGINN;
 
nul configuration() {   Série.commencer(115200);   pinMode(DCF_PIN, ENTRÉE);   pinMode(STATUS_PIN, SORTIE);   Série.println("Synchronisation");
}
 
nul boucle() {   si (BIT > 60) {nouvelle minute = faux;}   int DCF_SIGNAL = digitalRead(DCF_PIN);      si (DCF_SIGNAL == ÉLEVÉ && Signal == faux) {     Signal = vrai;      HIGH_Start = millis();      LOW_end = HIGH_Start;       LOW_time = LOW_end - LOW_Start;           si (nouvelle minute == vrai) {       Imprimer la description(BIT);       //Serial.print("Bit ");        //Serial.print (BIT);        //Serial.print (":");        TIME[BIT] = (BIT_time(LOW_time));       Série.imprimer (TIME[BIT]);       //Serial.println ();     }     d'autre {       Série.imprimer(".");     }   }     si (DCF_SIGNAL == FAIBLE && Signal == vrai) {     Signal = faux;      HIGH_end = millis();       LOW_Start = HIGH_end;      HIGH_time = HIGH_end - HIGH_Start;        NEUMINUTE(LOW_Zeit);   }
}
 
int BIT_Zeit (int LOW_Zeit) {    si (LOW_Zeit >= 851 && LOW_Zeit <= 950) {retour 0;}     si (LOW_Zeit >= 750 && LOW_Zeit <= 850) {retour 1;}    si (LOW_Zeit <= 350) {BIT-=1;retour "";}
}

nul NEUMINUTE (int LOW_Zeit) {   si (LOW_Zeit >= 1700) {     BIT = 0;     neueMinute = vrai;     ZEIT_STUNDE = ZEIT[29]*1+ZEIT[30]*2+ZEIT[31]*4+ZEIT[32]*8+ZEIT[33]*10+ZEIT[34]*20;     ZEIT_MINUTE = ZEIT[21]*1+ZEIT[22]*2+ZEIT[23]*4+ZEIT[24]*8+ZEIT[25]*10+ZEIT[26]*20+ZEIT[27]*40;     PAR_STUNDE = ZEIT[35];     PAR_MINUTE = ZEIT[28];     ZEIT_TAG = ZEIT[36]*1+ZEIT[37]*2+ZEIT[38]*4+ZEIT[39]*8+ZEIT[40]*10+ZEIT[41]*20;     ZEIT_MONAT = ZEIT[45]*1+ZEIT[46]*2+ZEIT[47]*4+ZEIT[48]*8+ZEIT[49]*10;     ZEIT_JAHR = 2000+ZEIT[50]*1+ZEIT[51]*2+ZEIT[52]*4+ZEIT[53]*8+ZEIT[54]*10+ZEIT[55]*20+ZEIT[56]*40+ZEIT[57]*80;     PAR_BEGINN = ZEIT[20];               Série.println();     Série.println("*****************************");     Série.imprimer ("Heure:");     Série.println();     Série.imprimer (ZEIT_STUNDE);     Série.imprimer (":");     Série.imprimer (ZEIT_MINUTE);     Série.println();     Série.println();     Série.imprimer ("Date:");     Série.println();     Série.imprimer (ZEIT_TAG);     Série.imprimer (".");     Série.imprimer (ZEIT_MONAT);     Série.imprimer (".");     Série.imprimer (ZEIT_JAHR);     Série.println();     Série.println("*****************************");        } d'autre {BIT++;}
}
 
nul Imprimer la description(int BitNumber) {   interrupteur (BitNumber) {     cas  0: Série.println("\ n # START MINUTE (IMMER 0)"); casser;     cas  1: Série.println("\ n # DATES MÉTÉOROLOGIQUES"); casser;     cas 15: Série.println("\ n # RUFBIT"); casser;     cas 16: Série.println("\ n # MEZ / MESZ"); casser;     cas 17: Série.println("\ n # MESZ"); casser;     cas 18: Série.println("\ n # MEZ"); casser;     cas 19: Série.println("\ n # SCHALTSEKUNDE"); casser;     cas 20: Série.println("\ n # INFORMATIONS SUR LA PAGE DE DÉPART (IMMER 1)"); casser;     cas 21: Série.println("\ n # MINUTES"); casser;     cas 28: Série.println("\ n # PARITA MINUTE"); casser;     cas 29: Série.println("\ n # SUPPORT");casser;     cas 35: Série.println("\ n # LE SOUTIEN PARITAIRE"); casser;     cas 36: Série.println("\ n # JOUR"); casser;     cas 42: Série.println("\ n # JOUR DU WEEK-END"); casser;     cas 45: Série.println("\ n # MONAT"); casser;     cas 50: Série.println("\ n # ANS"); casser;     cas 58: Série.println("\ n # DATE DE PARITÉ"); casser;   }
}

En tant que dépense dans Serial Monitor, nous obtenons maintenant la séquence de bits et le temps calculé.

 

Maintenant, vous pouvez également utiliser DCF77 pour vos propres projets. Vass Spass.

Für arduinoGrundlagen software

16 commentaires

Andreas Wolter

Andreas Wolter

@Ulrich Kreutzer: die Funktion hat den Rückgabedatentypen int. Aber in der Zeile wird versucht, einen String (bzw. const char*) zurück zu geben. Das ist nicht gut gelöst. Da 0 und 1 die anderen Varianten sind, würde ich versuchen, z.B. -1 zurück zu geben. Das müssten Sie allerdings dann auch beim Aufrufer so abfangen.

Grüße,
Andreas Wolter
AZ-Delivery Blog

Ulrich Kreutzer

Ulrich Kreutzer

Ich habe das gleiche Problem wie

Uli G.

Juni 12, 2020 at 19:57pm
Hallo ich würde gerne den ESP8266 benutzen.
wenn ich den Anruino ins Board wähle, wird das Übersetzen und Kompilieren abgeschlossen.
Benutze ich aber im Board den ESP8266 bekomme ich an der Zeile:

if (LOW_Zeit <= 350) {BIT-=1;return ""; den Fehler : invalid conversion from ‘const char*’ to ‘int’ [-fpermissive] was kann ich tun?

Leider gibt es im Kommentarbereich dafür keine Lösung. Ist vielleicht jetzt jemand da, der Hilfestellung geben kann?

Würde mich sehr freuen.

VG

Okin

Okin

Parity-Auswertung des DCF77-Signals ist unbeding notwendig werde ich baldigst nachrüsten!
Teste seit einigen Monaten ein System (ohne Parity-check) mit PRO-MINI328, DS3231, DCF77-Modul(Pollin), Anzeige: 7Segment und “umlaufende” Sekundenanzeige mit 60 Leds (über Schieberegisterkette). Zum Test wird stündlich mit DCF77 synchronisiert. Problem: Bastelkeller mit schlechtem DCF77-Signal. Ergebnis bisher: Es gab Tage (im Sommer?) da hat alles funktioniert – Uhrzeit war immer korrekt. Seit Herbst wurden immer mehr Störungen empfangen, die das Sync. unmöglich machten, bzw. es kam vor, dass “Zukunftsdaten” z.B. Jahr 56, etc. angezeigt wurden denke, dass dies mit Parity-Auwertung nich passieren kann. Auch kam es einige wenige male vor, dass sich der PRO-MINI328 “aufhing”, d.h. keine Anzeige; nach Hardware-Reset wurde die Zeit wieder richtig (RTC) angezeigt. Mir scheint, dass Microcontroller im Dauerbetrieb auch nicht ohne Watchdog laufen sollten….

Andreas Wolter

Andreas Wolter

@Abushammala: wir haben zur Zeit keinen Beitrag zum Raspi mit DCF77. Daher habe ich für Sie einen externen Link rausgesucht. Vielleicht hilft Ihnen das weiter:
https://forum-raspberrypi.de/forum/thread/35453-dcf77-uhr-unter-jessie/

Grüße,
Andreas Wolter
AZ-Delivery Blog

Abushammala

Abushammala

Hallo,

ich finde bei Ihnen Keine Anleitung, wie man die DCF77 mit Raspberry pi verbinden kann. Könnten Sie mir den Konfigurationscode zukommen lassen.

Vielen Dank

Joschua

Joschua

Danke für den Tollen Code.
Ich hatte anfangs das Problem das bei mir keine DCF77 Library funktioniert hatte.
Aber mit diesem Code hat fast alles problemlos funktioniert.
Das einzige was ich ändern musste waren die folgenden werte. (Bereits geändert)
if (LOW_Zeit >= 821 && LOW_Zeit <= 950) {
return 0;
}
if (LOW_Zeit >= 750 && LOW_Zeit <= 820) {
return 1;
}
if (LOW_Zeit <= 350) {
BIT -= 1;
return "";
}

Bei diesen werten sollte man einfach ausprobieren bis es bei einem passt.

Max

Max

Ich nochmal. Schade das du die Parity-Bits in deinem Code zwar liest gar nicht verwendest. Ich habe das NEUEMINUTE etwas abgeändert, nun checkt es die 3 Parität-bits, baut noch Hürden für den Wertebereich mit ein (z.B. keine Minute über 59 erlaubt) und für das erfüllen des DCF_stable bit muss noch 3x das selbe Ergebnis hintereinander empfangen werden (ohne Minute, die kann mit dem DCF_minute bit schon vorher verwendet werden):

void NEUMINUTE (int LOW_Zeit) {
int temp_min=0; //save bit count of minute part of ZEIT
int temp_hour=0;//save bit count of hour part of ZEIT
int temp_year=0;//save bit count of year part of ZEIT

if (LOW_Zeit >= 1700) { BIT = 0; ZEIT_STUNDE = ZEIT29 * 1 + ZEIT30 * 2 + ZEIT31 * 4 + ZEIT32 * 8 + ZEIT33 * 10 + ZEIT34 * 20; ZEIT_MINUTE = ZEIT21 * 1 + ZEIT22 * 2 + ZEIT23 * 4 + ZEIT24 * 8 + ZEIT25 * 10 + ZEIT26 * 20 + ZEIT27 * 40; PAR_STUNDE = ZEIT35; PAR_MINUTE = ZEIT28; ZEIT_WOCHENTAG = ZEIT42 * 1 + ZEIT43 * 2 + ZEIT44 * 4; ZEIT_TAG = ZEIT36 * 1 + ZEIT37 * 2 + ZEIT38 * 4 + ZEIT39 * 8 + ZEIT40 * 10 + ZEIT41 * 20; ZEIT_MONAT = ZEIT45 * 1 + ZEIT46 * 2 + ZEIT47 * 4 + ZEIT48 * 8 + ZEIT49 * 10; ZEIT_JAHR = 2000 + ZEIT50 * 1 + ZEIT51 * 2 + ZEIT52 * 4 + ZEIT53 * 8 + ZEIT54 * 10 + ZEIT55 * 20 + ZEIT56 * 40 + ZEIT57 * 80; PAR_BEGINN = ZEIT20; //DCF PAR bit is calculatet ‘even’, so we need to add 1 temp_min=ZEIT21 + ZEIT22 + ZEIT23 + ZEIT24 + ZEIT25 + ZEIT26 + ZEIT27+PAR_MINUTE+1; temp_hour=ZEIT29 + ZEIT30 + ZEIT31 + ZEIT32 + ZEIT33 + ZEIT34+PAR_STUNDE+1; temp_year=ZEIT50 + ZEIT51 + ZEIT52 + ZEIT53 + ZEIT54 + ZEIT55 + ZEIT56 + ZEIT57+PAR_BEGINN+1; //bitread only reads one specific bit – since we fiddeld with the temp value, even/odd decides if its valid or false if ((bitRead(temp_min, 0)) && (bitRead(temp_hour, 0)) && (ZEIT_STUNDE <= 23) && (ZEIT_STUNDE >= 0) && (ZEIT_MINUTE <= 60) && (ZEIT_MINUTE >= 0) && (ZEIT_TAG <= 31) && (ZEIT_TAG >= 1) && (ZEIT_MONAT <= 12) && (ZEIT_MONAT >= 1) && (ZEIT_JAHR <= 2040) && (ZEIT_JAHR >= 2020) && (ZEIT_WOCHENTAG <= 7) && (ZEIT_WOCHENTAG >= 1 )) { DCF_minute= true; //check if there is rubbish in the signal: Store last 3 values of year, month, day, weekday. Only if all three are equal, neueMinute is set to true if ((bitRead(temp_year, 0)) && (ZEIT_JAHR_veryoldZEIT_JAHR_old) && (ZEIT_JAHR_veryoldZEIT_JAHR) && (ZEIT_MONAT_veryoldZEIT_MONAT_old) && (ZEIT_MONAT_veryoldZEIT_MONAT) && (ZEIT_TAG_veryoldZEIT_TAG_old) && (ZEIT_TAG_veryoldZEIT_TAG) && (ZEIT_WOCHENTAG_veryoldZEIT_WOCHENTAG_old) && (ZEIT_WOCHENTAG_veryoldZEIT_WOCHENTAG)&& (ZEIT_STUNDE_veryoldZEIT_STUNDE_old) && (ZEIT_STUNDE_veryoldZEIT_STUNDE)) { DCF_stable = true; } else { DCF_stable = false; } } else { DCF_minute_hook = false; } ZEIT_JAHR_veryold=ZEIT_JAHR_old; ZEIT_JAHR_old=ZEIT_JAHR; ZEIT_MONAT_veryold=ZEIT_MONAT_old; ZEIT_MONAT_old=ZEIT_MONAT; ZEIT_TAG_veryold=ZEIT_TAG_old; ZEIT_TAG_old=ZEIT_TAG; ZEIT_WOCHENTAG_veryold=ZEIT_WOCHENTAG_old; ZEIT_WOCHENTAG_old=ZEIT_WOCHENTAG; ZEIT_STUNDE_veryold=ZEIT_STUNDE_old; ZEIT_STUNDE_old=ZEIT_STUNDE; } else { BIT++; }

}

Max

Max

Vielen Dank fr den tollen Code, der ist wirklich leicht nachzuvollziehen und gut in das eigene Projekt zu intregrieren. Ich werkle noch an einer Fehlerkorrektur um die ganzen bit-Kipper raus zu bekommen, aber es zeigt schonmal zu 70% aller Fälle eine genaue Zeit an :)

Uli G.

Uli G.

Hallo ich würde gerne den ESP8266 benutzen.
wenn ich den Anruino ins Board wähle, wird das Übersetzen und Kompilieren abgeschlossen.
Benutze ich aber im Board den ESP8266 bekomme ich an der Zeile:

if (LOW_Zeit <= 350) {BIT-=1;return ""; den Fehler : invalid conversion from ‘const char*’ to ‘int’ [-fpermissive] was kann ich tun?
WO

WO

Info für Thomas:
Veriablendeklaration:
int ZEIT_SEKUNDE;
Hallo Thomas
Behelfsweise Variable “ZEIT_SEKUNDE” in funktion einfügen und dieser die Bitnummer+1 zuweisen.
Allerdings: Sekunde 59 dauert dann 2 Sekunden!
void PrintBeschreibung(int BitNummer) {
ZEIT_SEKUNDE = BitNummer+1;

Für eine exakte Sekundenanzeige 58, 59 muss der Code wohl etwas tiefergehend erweitert werden.
WO

Thomas

Thomas

funktioniert tatelos. Möchte auf Basis dieses Sketches eine NIXIE Uhr aufbauen. Mir fehlen die Sekundenanzeige. Da ich Programmieranfänger bin wäre ich dankbar wenn mir Jemand Tips geben könnte wie der Seketch zecks Sekndenanzeige zu erweitern ist.

Danke

Gunther Jordan

Gunther Jordan

Leider ist der Fehler, den Jörg Schnur letztes Jahr aufgezeigt hat immernoch drin.
Weiterhin ist eine ganz wichtige Information unterschlagen: Sekunde 59 wird nicht markiert! D.h. in dieser Sekunde wird das Sendesignal nicht moduliert, weder eine “1” noch eine “0” gesendet. DARAN erkennt der Empfänger den Beginn der vollen Minute! Und nicht etwa an der “0” am Anfang…
Die Umstellung zwischen Sommer und Normalzeit sowie die Schaltsekunde werden 1 Stunde im Voraus angezeigt, im Falle der Schaltsekunde ist die 59. Sekunde eine “0” und die 60. Sekunde wird keine Marke gesendet.

Ein Nutzer

Ein Nutzer

Der Sketch setzt bei meinem Modul (Conrad BN641138) vorraus, dass das Signal am invertierenden Ausgang abgegriffen wird.
Um dieses zu umgehen und den normalen DCF Ausgang auf Pin3 am Modul zu verwenden, kann man auch folgende Zeile:

int DCF_SIGNAL = digitalRead(DCF_PIN);

durch dieses ersetzen:

int DCF_SIGNAL_inverted = digitalRead(DCF_PIN);
int DCF_SIGNAL = (!DCF_SIGNAL_inverted);

Anschließend läuft der Sketch ohne Probleme!

Jörg Schnur

Jörg Schnur

Danke für die super Informationen. Müßte es nicht 60 Sekunden heißen im Satz “Der DCF77 Sender sendet in 60 Minuten mehrere Informationen.”?

Mertke

Mertke

Hallo, ein super Sketch. Der beste den ich bis jetzt hatte.
Ich bastel an einer Bewässerungsanlage mit 16 Ausgängen und 2 Zeiten pro Tag mit einer RTC DS3231. Ich hätte gerne eine zusätzliche DCF 77 Zeit.
Ich habe ein Modul von Pollin mit 3,3V und dahinter einen Verstärker mit Cmos IC 4093
(http://amateurfunkbasteln.de/dcf77/ )
Leider funktioniert bei mir nur ein keiner Teil und nur nach ändern der LOW Zeiten.
//if (LOW_Zeit >= 851 && LOW_Zeit <= 950) {return 0;} Original
if (LOW_Zeit >= 751 && LOW_Zeit <= 950) {return 0;} meine Eingabe
//if (LOW_Zeit >= 750 && LOW_Zeit <= 850) {return 1;} Original
if (LOW_Zeit >= 450 && LOW_Zeit <= 750) {return 1;} meine Eingabe
if (LOW_Zeit <= 350) {BIT-=1;return "";}
Auf dem seriellen Monitor kommt nur die Anzeige
Syncronisierung
18:54:22.126 ………… Die Urzeit funktioniert aber kein Datum

Ich nutze einen Arduino UNO am digitalen Pin 2 mit externem 10KOHM Widerstand gegen 5Volt. Anzeigen möchte ich die Uhrzeit auf einem Display 16×2 oder 20×4

Um Störimpulse zu verringern habe ich einen Festspannungsregler L4940V5 mit 5Volt aufgebaut an einem 12V AKKU
Wenn ich diese 5 Volt nutze habe ich aber keinen seriellen Monitor mehr.

Ich habe einige Schwierigkeiten mit dem Programmieren und lerne immer ein bischen mehr. Meistens versthe ich langsam die Beispiele und ändere den Sketch dann für mich um.

Ich würde mich über jede Hilfe sehr freuen
schönen Abend
Fredy

Fred

Fred

Hallo, guter Sketch zur Abfrage der Zeit und auch für einen Laien wie mich nachvollziehbar. Ich hab in gleich getestet nachdem ich ihn im Netz gefunden hatte. Mein DCF77 Modul hat sofort funktioniert. Probleme gab es aber beim kopieren des Sketches mit der Kopierfunktion in die Arduino-IDE. Beim Kompilieren kamen jede Menge stray ‘\’ Fehlermeldungen. Erst durch Laden des Textes in Notepad++ (ANSI-Kodierung) und entfernen einiger komischer Zeichen funktionierte es dann. Ich würde auch gern noch wissen, wozu die Status-LED (Pin13) gedacht war. Gratuliere AZ-Delivery zu den vielen guten Beispielen, weiter so.

Laisser un commentaire

Tous les commentaires sont modérés avant d'être publiés

Articles de blog recommandés

  1. ESP32 jetzt über den Boardverwalter installieren - AZ-Delivery
  2. Internet-Radio mit dem ESP32 - UPDATE - AZ-Delivery
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1 - AZ-Delivery
  4. ESP32 - das Multitalent - AZ-Delivery