Wie funktionieren eigentlich die funkgesteuerten Uhren, die automatisch die korrekte Uhrzeit und das Datum anzeigen?
Warum benötigen diese immer so lange, nach einem Batteriewechsel, bis diese wieder die Uhrzeit anzeigen und wie kann ich das selber mit einem Arduino erstellen?
Damit diese Funkuhren funktionieren, nutzt man die Zeitzeichen des Langwellensender DCF77 aus Mainflingen bei Frankfurt, der auf der Frequenz 77,5KHz diese Informationen funkt.
Quelle: DCF77 – Wikipedia und DCF77 – PTB.de
Verwendete Hardware:
Arduino Uno
DCF77 Antenne 77,5Khz
Flüssigkristallanzeige LCD 1602
I2C Adapter LCM1602
Verdrahtung:

Quellcode und Bibliothek:
„DCF77“ Library von Thijs Elenbaas in der Version 1.0.0
„Time“ Library von Paul Stoffregen in der Version 1.6.0
Datenblatt: DCF77-Modul

Decodierung:

Signalunterbrechung 100ms = 0, 200ms = 1 | ||||
Bit (Sek) | Bedeutung der Werte | Beispiel (21.10.2021 23:55Uhr) | ||
60 | Start der Minute „0“ oder (kein Signal bei Schaltsekunde – Dann aber Sek 59 eine = 0) | 0 | Sek 59 hatte kein Signal | |
1 bis 14 | seit Dez. 2006: Wetterinformationen, sowie Informationen des Katastrophenschutzes | |||
15 | Rufbit (bis Mitte 2003 Reserveantenne) | 0 | Anlage OK | |
16 | „1“: Am Ende dieser Stunde wird MEZ/MESZ umgestellt. | 0 | Keine Umstellung | |
17 | Mitteleuropäische Zeit / Sommerzeit | 1 | Sommerzeit | |
18 | „01“: MEZ oder „10“: MESZ | 0 | ||
19 | „1“: Am Ende dieser Stunde wird eine Schaltsekunde eingefügt.(eine Stunde vorher) | 0 | Keine Umstellung | |
20 | Immer „1“: Beginn der Zeitinformation | 1 | Immer | |
21 | Minute (Einer) | Bit für 1 | 1 | 1010101 = 55 |
22 | Bit für 2 | 0 | ||
23 | Bit für 4 | 1 | ||
24 | Bit für 8 | 0 | ||
25 | Minute (Zehner) | Bit für 10 | 1 | |
26 | Bit für 20 | 0 | ||
27 | Bit für 40 | 1 | ||
28 | Parität Minute | 0 | 4 Einser-Bits durch zwei = 2,0 | |
29 | Stunde (Einer) | Bit für 1 | 1 | 110001 = 23 |
30 | Bit für 2 | 1 | ||
31 | Bit für 4 | 0 | ||
32 | Bit für 8 | 0 | ||
33 | Stunde (Zehner) | Bit für 10 | 0 | |
34 | Bit für 20 | 1 | ||
35 | Parität Stunde | 1 | 3 Einser-Bits durch zwei = 1,5 | |
36 | Kalendertag (Einer) | Bit für 1 | 1 | 100001 = 21 |
37 | Bit für 2 | 0 | ||
38 | Bit für 4 | 0 | ||
39 | Bit für 8 | 0 | ||
40 | Kalendertag (Zehner) | Bit für 10 | 0 | |
41 | Bit für 20 | 1 | ||
42 | Wochentag (Mo=1, So=7) | Bit für 1 | 0 | 001 = 4 (Donnerstag) |
43 | Bit für 2 | 0 | ||
44 | Bit für 4 | 1 | ||
45 | Monat (Einer) | Bit für 1 | 0 | 00001 = 10 |
46 | Bit für 2 | 0 | ||
47 | Bit für 4 | 0 | ||
48 | Bit für 8 | 0 | ||
49 | Monat (Zehner) | Bit für 10 | 1 | |
50 | Jahr (Einer) | Bit für 1 | 1 | 10000100 = 21 |
51 | Bit für 2 | 0 | ||
52 | Bit für 4 | 0 | ||
53 | Bit für 8 | 0 | ||
54 | Jahr (Zehner) | Bit für 10 | 0 | |
55 | Bit für 20 | 1 | ||
56 | Bit für 40 | 0 | ||
57 | Bit für 80 | 0 | ||
58 | Parität Datum | 0 | 6 Einser-Bits durch zwei = 3,0 | |
59 | Runde beendet | In der Regel Signalpause (außer Schaltsekunde wird 0 benötigt) | 1000ms kein Signal |
Quellcode1:
#define BLINKPIN 13
#define DCF77PIN 2
#define PIN_schalter 7
int prevSensorValue=0;
void setup() {
Serial.begin(9600);
pinMode(DCF77PIN, INPUT);
pinMode(13, OUTPUT);
pinMode(PIN_schalter, OUTPUT);
digitalWrite(PIN_schalter, LOW); // DCF77 Modul wird hiermit gestartet!
Serial.println("0ms 100ms 200ms 300ms 400ms 500ms 600ms 700ms 800ms 900ms 1000ms 1100ms 1200ms");
}
void loop() {
int sensorValue = digitalRead(DCF77PIN);
if (sensorValue==1 && prevSensorValue==0) { Serial.println(""); }
digitalWrite(BLINKPIN, sensorValue);
Serial.print(sensorValue);
prevSensorValue = sensorValue;
delay(10);
}
Aus der Beispiel-Liste der DCF77 Library das Script „DCF77Signal“, um die PIN_schalter Funktion erweitert.
Wichtig ist, das vorab mit diesem Code die Funktionalität des DCF77 Modul getestet wird.
Sind die kurzen und die langen Zeitzeichen, gleichmäßig kurz und lang, dann ist das Signal brauchbar und auswertbar.

Sollten die Längen unregelmäßig aussehen, dann einen störungsfreien Platz für das Modul suchen.
Quellcode2:
#include "DCF77.h"
#include "TimeLib.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define DCF_PIN 2 // Connection pin to DCF 77 device
#define DCF_INTERRUPT 0 // Interrupt number associated with pin
#define PIN_LED 13
#define PIN_schalter 7
LiquidCrystal_I2C lcd(0x27,16,2);
//time_t time;
DCF77 DCF = DCF77(DCF_PIN, DCF_INTERRUPT);
// wurde ein gueltiges Signal gefunden
bool g_bDCFTimeFound = false;
void setup()
{ lcd.init();
pinMode(PIN_LED, OUTPUT);
pinMode(PIN_schalter, OUTPUT);
digitalWrite(PIN_schalter, LOW);
Serial.begin(9600);
DCF.Start();
Serial.println("Warten auf DCF77-Zeit... ");
Serial.println("Dies dauert mindestens 2 Minuten, in der Regel eher länger.");
lcd.backlight();
lcd.setCursor(1,0);
lcd.print("Warte auf Zeit!");
lcd.setCursor(1,1);
lcd.print("3 Min Geduld!");
delay(2000);
lcd.clear();
}
void loop()
{
// das Signal wird nur aller 5 Sekunden abgefragt
delay(950);
digitalWrite(PIN_LED, HIGH);
delay(50);
digitalWrite(PIN_LED, LOW);
time_t DCFtime = DCF.getTime(); // Check if new DCF77 time is available
if (DCFtime!=0)
{
Serial.println("Time is updated");
setTime(DCFtime);
g_bDCFTimeFound = true;
lcd.clear();
}
// die Uhrzeit wurde gesetzt, also LED nach kurzer Zeit ein
if (g_bDCFTimeFound)
{
delay(50);
digitalWrite(PIN_LED, HIGH);
}
digitalClockDisplay();
}
void digitalClockDisplay()
{
// digital clock display of the time
printDigits(hour());
Serial.print(":");
printDigits(minute());
Serial.print(":");
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
lcd.setCursor(0,0);
printDigits2(hour());
lcd.print(":");
printDigits2(minute());
lcd.print(":");
printDigits2(second());
lcd.setCursor(0,1);
lcd.print(day());
lcd.print(" ");
lcd.print(month());
lcd.print(" ");
lcd.print(year());
}
void printDigits(int digits)
{
// utility function for digital clock display: prints preceding colon and leading 0
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
void printDigits2(int digits)
{
// utility function for digital clock display: prints preceding colon and leading 0
if(digits < 10)
lcd.print('0');
lcd.print(digits);
}
In dem Video werden gesammelte Hintergrundinformationen zusammen getragen und gezeigt wie die einzelnen Signale in Informationen umcodiert werden.
Nicht im Video zu sehen
Variante 2:
Mit Wochentag auf 4 Zeilen LCD
#include "DCF77.h"
#include "TimeLib.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define DCF_PIN 2 // DCF 77 Modul
#define DCF_INTERRUPT 0 // Interrupt number associated with pin
#define PIN_LED 13
#define PIN_schalter 7
LiquidCrystal_I2C lcd(0x27,20,4);
DCF77 DCF = DCF77(DCF_PIN, DCF_INTERRUPT);
// wurde ein gueltiges Signal gefunden
bool g_bDCFTimeFound = false;
char Wochentag[20];
const char* wochentage[] = {„Sontag“, „Montag“, „Diensttag“, „Mittwoch“, „Donnerstag“, „Freitag“, „Samstag“};
int wochentag_zahl = weekday();
void setup()
{ lcd.init();
pinMode(PIN_LED, OUTPUT);
pinMode(PIN_schalter, OUTPUT);
digitalWrite(PIN_schalter, LOW);
Serial.begin(9600);
DCF.Start();
Serial.println("Warten auf DCF77-Zeit... ");
Serial.println("Dies dauert mindestens 2 Minuten, in der Regel eher länger.");
lcd.backlight();
lcd.setCursor(1,0);
lcd.print("Warte auf Zeit!");
lcd.setCursor(1,1);
lcd.print("3 Min Geduld!");
delay(2000);
lcd.clear();
}
void loop()
{
// das Signal wird nur aller 5 Sekunden abgefragt
delay(950);
digitalWrite(PIN_LED, HIGH);
delay(50);
digitalWrite(PIN_LED, LOW);
time_t DCFtime = DCF.getTime(); // Check if new DCF77 time is available
if (DCFtime!=0)
{
Serial.println("Time is updated");
setTime(DCFtime);
g_bDCFTimeFound = true;
lcd.clear();
}
// die Uhrzeit wurde gesetzt, also LED nach kurzer Zeit ein
if (g_bDCFTimeFound)
{
delay(50);
digitalWrite(PIN_LED, HIGH);
}
set_date_string(date_string);
digitalClockDisplay();
}
void digitalClockDisplay()
{
// digital clock display of the time
printDigits(hour());
Serial.print(":");
printDigits(minute());
Serial.print(":");
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
lcd.setCursor(0,0);
printDigits2(hour());
lcd.print(":");
printDigits2(minute());
lcd.print(":");
printDigits2(second());
lcd.setCursor(0,1);
lcd.print(day());
lcd.print(".");
lcd.print(month());
lcd.print(".");
lcd.print(year());
lcd.setCursor(0,2);
lcd.print(date_string);
}
void printDigits(int digits)
{
// Utility-Funktion für Digitaluhr-Anzeige: druckt vorangestellten Doppelpunkt und führende 0
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
void printDigits2(int digits)
{
if(digits < 10)
lcd.print('0');
lcd.print(digits);
}
void set_date_string(char date_string[]) {
int weekday_num = weekday();
int position = 0;
if (weekday_num == 0) {
return;
}
strcpy(date_string, weekdays[weekday_num - 1]);
}
Noch nicht schön, aber schonmal funktionabel
