Funkgesteuerte Uhr mit Arduino erstellen

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

DCF77-Modul Pin-Belegung

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

15 Kommentare zu “Funkgesteuerte Uhr mit Arduino erstellen”
  1. Hi,
    ich habe probleme beim kompilieren.
    könnten sie mit dabei bitte helfen?
    order können sie mir ihren code mit den headerfiles schicken?

    Fehlermeldung:
    Arduino: 1.8.19 (Windows Store 1.8.57.0) (Windows 10), Board: „Arduino Uno“

    C:\Users\Mustafa\OneDrive – iem.thm.de\Dokumente\Arduino\libraries\jm_LiquidCrystal_I2C\I2CIO.cpp:29:10: fatal error: jm_Scheduler.h: No such file or directory

    #include

    ^~~~~~~~~~~~~~~~

    compilation terminated.

    exit status 1

    Fehler beim Kompilieren für das Board Arduino Uno.

    Dieser Bericht wäre detaillierter, wenn die Option
    „Ausführliche Ausgabe während der Kompilierung“
    in Datei -> Voreinstellungen aktiviert wäre.

    • Hallo Mustafa,

      sieht so aus als liegen deine Librarys in der Microsoft Cloud OneDrive. Das macht meistens Ärger.

      Installiere Arduino neu, mit dieser PortableIDE-Beschreibung und probiere es erneut.

      https://www.arduino.cc/en/Guide/PortableIDE

      So in etwa sollten diese wichtigen Dateien dann lokal angeboten werden: C:\arduino-portable\arduino-1.8.19\libraries\LiquidCrystal_I2C

      Ich hoffe das bringt dich weiter.

      VG Prilchen

  2. Hallo,
    bin bei den ersten Versuchen mit Arduino und wollte die DCF77 Uhr aufbauen.
    Leider kommt nachfolgende Fehlermeldung.
    Kann mir da vielleicht jemand einen Rat geben was ich machen könnte??

    Arduino: 1.8.19 (Windows Store 1.8.57.0) (Windows 10), Board: „Arduino Uno“

    *** Nachricht stark gekürzt ****

    time_t currentTime =latestupdatedTime – UTCTimeDifference + (now() – processingTimestamp);

    ^~~

    pow

    Bibliothek DCF77 in Version 1.0.0 im Ordner: C:\Users\X250\Documents\Arduino\libraries\DCF77 wird verwendet

    Bibliothek Time in Version 1.6.1 im Ordner: C:\Users\X250\Documents\Arduino\libraries\Time wird verwendet

    Bibliothek Wire in Version 1.0 im Ordner: C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.57.0_x86__mdqgnx93n4wtt\hardware\arduino\avr\libraries\Wire wird verwendet

    Bibliothek LiquidCrystal_I2C in Version 1.1.2 im Ordner: C:\Users\X250\Documents\Arduino\libraries\LiquidCrystal_I2C wird verwendet

    exit status 1

    Fehler beim Kompilieren für das Board Arduino Uno.

    • Hallo Rudi,

      du hast die Time Library in der neusten Version 1.6.1 installiert. Die läuft nicht!
      Die Version 1.6.0 von Paul Stoffregen installieren und schon müsste es laufen. 🙂

      VG Prilchen

  3. Hallo Prilchen,
    ich möchte gern die dcf77 Uhr nachbauen.
    Leider kommt immer die Meldung
    „Fehler beim Kompilieren für das Board Arduino Uno“
    Was mache ich falsch?
    für Hilfe diesbezüglich wäre ich dankbar.
    J.Dorn

    • Hallo Jürgen,

      schau mal ob du die Time Library von Paul Stoffregen in der neusten Version 1.6.1 installiert hast. Die läuft nicht!
      Wenn ja bitte einmal downgraden auf Version 1.6.0

      VG Prilchen

  4. Hallo,
    danke für die Hilfe,
    ich kann jetzt den Sketch hochladen.
    aber mein DCF Modul ist von ELV und hat nur drei Anschlüsse.
    Was mache ich mit dem Schalter Pin 7?
    Nach 10 min.immer noch keine Zeitanzeige.
    J.Dorn

    • Hallo Jürgen,
      sehr schön, wieder ein Schritt weiter.
      Am besten mal beim Lieferanten das Datenblatt besorgen und dann die von Hersteller beschreibe Funktionalität und notwendige Betriebsspannung prüfen.
      Auch mal testweise draußen versuchen in Betrieb zu nehmen
      VG Prilchen

  5. Hallo,
    danke nochmals für die Hilfe.
    Es funktioniert jetzt alles.
    Grüße Jürgen

  6. Hallo Prilchen,
    die DCF77 Uhr läuft seit Tagen ohne Macken.
    Meine Frage ist es möglich noch den Wochentag mit an zu zeigen (Mo,Di.usw)?
    LCD 20mal4 habe ich schon im Sketch eingebunden.
    Jürgen

    • Hallo Jürgen,

      klar das geht. Ist ja im 42-44 Bit auslesbar.
      Wenn du in die Library schaust, dann siehst du, das der Variable-Name dafür „weekday“ ist.
      Diese gibt den Wochentag als Zahl zurück.
      Somit musst du nur einen Array erzeugen, der alle 7 Wochentage in der richtigen Reihenfolge enthält.

      Beispiel:
      char Wochentag[20];
      const char* wochentage[] = {„Sontag“, „Montag“, „Diensttag“, „Mittwoch“, „Donnerstag“, „Freitag“, „Samstag“};
      int wochentag_zahl = weekday();
      strcpy(Wochentag, wochentage[wochentag_zahl – 1]);

      und lässt dir diesen Wochentag im Display anzeigen

      VG Prilchen

  7. Hallo Prilchen,

    leider bin ich blutiger Anfänger in Sachen Arduino und C++.
    Kannst du mir diesbezüglich weiter helfen?
    Jürgen

    • Guten Morgen Jürgen, der Spaß darin, sich mit diesen Microcontrollern zu beschäftigen ist, die vor einem liegenden Hardware mit seinen eigenen Skripten zum Funktionieren zu bringen.
      Du wirst also nicht drum rum kommen, dich Schritt für Schritt in die Themen Microcontroller, Aktoren, Sensoren, Entwicklungsumgebung etc. einzulesen.
      Hier empfehle ich das Buch https://www.rheinwerk-verlag.de/arduino-das-umfassende-handbuch/

      VG Prilchen

      PS: Auch wenn ich deine Hardware hier nicht vor mir liegen habe, probiere mal den Quellcode der Variante 2 🙂

  8. Arduino: 1.8.15 (Windows 10), Board: „Arduino Nano, ATmega328P (Old Bootloader)“
    Hallo Prilchen,
    ich wollte die Variante 2 ausprobieren,es kommt aber eine Meldung „stray 342 in program“
    Grüße Jürgen

    • Hallo Jürgen, hatte mir die Mühe gemacht und die Entwicklungsumgebung mit der mir vorliegen Hardware nachzustellen. Damit hatte ich in der dritten Zeile dann den Werktag angezeigt bekommen. Schade das sie bei dir nicht funktioniert. Da ich deine Entwicklungsumgebung nicht kenne, kann ich leider nicht mehr für dich tun.

      VG Prilchen

Kommentare sind geschlossen.