Hauptmenue
Netcoast.ch
Mikrocontroller
04.10.2024 - Neues Skript für den DCF77 Empfang welches komplett ohne spezielle Library auskommt und einfach die Zeit bezieht und ausgibt.
Beim vorher verwendeten Skript, welches nicht von mir war, hat mich gestört, dass zum einen auf eine veraltete Library verwiesen wurde und ich zu wenig erkennen konnte, was wo passiert.

Der Funkuhr-Zeitempfang ist eine praktische Lösung, wenn man sein Gerät nicht ans Internet hängen möchte und doch auf aktuelle Zeiten zugreifen will.
Das untenstehende Beispiel funktioniert ebenso mit anderen Mikrocontroller (ESP8266, ESP32S, Arduino Nano, Arduino Mega, Arduino Micro usw...), man muss den Signaleingang einfach an einen Interrupt-Fähigen Eingang hängen.

Schema Verbindung DCF77-Modul von ELV und Arduino UNO

Arduino UNO Digital-PIN Nr. 2 gem. Beispielcode.
Sollte ein Interrupt-Fähiger Eingang sein.
Arduino UNO 5 Volt Power-PIN und Arduino UNO GND (ev. externes Netzteil hier zwischenschalten).
Erklärung verbindungen auf dem Breadboard:
SIGNAL-Leitung vom DCF77-Modul von ELV wird über eine sogenannte Open Collector Verbindung mit dem Arduino UNO am PIN 2 verbunden.
Das heisst der PIN 2 wird nicht direkt mit dem SIGNAL-PIN vom DCF77-Modul verbunden, sondern es wird eine Verbindung zum 5 Volt-Strom über einen 10K Ohm Widerstand dazwischen gesetzt.
DCF77-Modul von ELV
+ PIN wird direkt mit 5 Volt verbunden.
GND PIN wird direkt mit GND verbunden.
SIG-PIN (SIGNAL) wird über den Open Collector Teil verbunden.

Zwischen GND und SIGNAL sollte ein 10nF Keramik Kondensator gelötet werden um Störsignale aus der Stromquelle zu korrigieren.
Ich habe dies auf der Rückseite gemacht, damit die PIN's über die Dupont-Kabel verbunden werden können.

Beispielcode DCF77 Zeit auslesen mit Arduino UNO

Hinweis
Wenn man nach 10 Minuten immer noch keine Zeit erhalten hat, kann dies am Empfang innerhalb vom Gebäude liegen.
In diesem Fall mal den Standort wechseln und es erneut versuchen. Eventuell ist aber auch Wetterbedingt der Sender abgeschaltet.
Ich hatte entweder nach 3 Minuten eine Zeit erhalten oder nie...
Im Angehängten Sketch und Code-Beispiel unten wird sekündlich der Empfangene Wert ausgegeben (wenn Pin 4 kurz auf LOW gezogen wurde).
Wenn also im Serial-Monitor nichts erscheint, oder die Daten sehr unregelmässig reinkommen, hat man entweder: keinen Empfang, Signalkabel falsch angeschlossen, zu wenig Leistung (in dem Fall auf ein externes Netzteil wechseln).

Arduino UNO Digital-PIN Nr. 2 gem. Beispielcode.
Arduino UNO 5 Volt Power-PIN und Arduino UNO GND (ev. externes Netzteil hier zwischenschalten).

Erklärung verbindungen auf dem Breadboard:
SIGNAL-Leitung vom DCF77-Modul von ELV wird über eine sogenannte Open Collector Verbindung mit dem Arduino UNO am PIN 2 verbunden.
Das heisst der PIN 2 wird nicht direkt mit dem SIGNAL-PIN vom DCF77-Modul verbunden, sondern es wird eine Verbindung zum 5 Volt-Strom über einen 10K Ohm Widerstand dazwischen gesetzt.

DCF77-Modul von ELV
+ PIN wird direkt mit 5 Volt verbunden.
GND PIN wird direkt mit GND verbunden.
SIG-PIN (SIGNAL) wird über den Open Collector Teil verbunden.

Zwischen GND und SIGNAL sollte ein 10nF Keramik Kondensator gelötet werden um Störsignale aus der Stromquelle zu korrigieren.
Ich habe dies auf der Rückseite gemacht, damit die PIN's über die Dupont-Kabel verbunden werden können.

Beispielcode DCF77 Zeit auslesen mit Arduino UNO

Hinweis
Wenn man nach 10 Minuten immer noch keine Zeit erhalten hat, kann dies am Empfang innerhalb vom Gebäude liegen.
In diesem Fall mal den Standort wechseln und es erneut versuchen. Eventuell ist aber auch Wetterbedingt der Sender abgeschaltet.
Ich hatte entweder nach 3 Minuten eine Zeit erhalten oder nie...
Im Angehängten Sketch und Code-Beispiel unten wird sekündlich der Empfangene Wert ausgegeben (wenn Pin 4 kurz auf LOW gezogen wurde).
Wenn also im Serial-Monitor nichts erscheint, oder die Daten sehr unregelmässig reinkommen, hat man entweder: keinen Empfang, Signalkabel falsch angeschlossen, zu wenig Leistung (in dem Fall auf ein externes Netzteil wechseln).

// 03.10.2024 - slaps313
// Dieser Code demonstriert das Beziehen der DCF77 Zeit und das Umwandeln eines komplett erhaltenen Zeit-Telegramms.
// Er soll als Grundlage dienen um sich seine eigene Zeitsynchronisation umzusetzen.
//
// Erklärungen und Tipps:
//
// Equipment:
//
// DCF77 Empfangsmodul (ich habe es mit dem von ELV getestet).
// Das Skript habe ich mit diversen Geräte testen können: Arduino Uno, Arduino Mega, Arduino Nano, Arduino Micro, ESP32S
//
// Zeitsignal empfangen (inkl. meine Erfahrungen):
//
// Erhält man keine Zeit weil die Zeitbits nicht Sekündlich gelesen werden können, kann es daher kommen, dass man zu wenig Leistung für die Antenne und den Mikrocontroller liefert.
// Mein ELV-Empfangsgerät benötigte jeweils 5 Volt und je nach Mikrocontroller eine Zusätzliche Spannungsversorgung (rein über USB-Port reichte nicht).
// Das Skript geht davon aus, dass die Zeitbits mit "LOW" daher kommen. Bei einem invertierten Signal, muss man entsprechend umdenken.
// Je nach Witterung kann die Sendeantenne in Deutschland komplett abgestellt sein!
// Je nach Witterung erhält man kein zuverlässiges Signal.
// In der Theorie ist es ein exaktes Signal, ich würde aber für ein 0-Bit LOW von 30 - 190 ms ausgehen und für ein 1-Bit LOW von 190 - 500 ms ausgehen.
//
// 100 ms LOW heisst: Bit = 0
// 200 ms LOW heisst: Bit = 1
//
// Das Zeitsignal wird während einer Minute im Sekundentakt (1 Bit pro Sekunde) übertragen (Bit 0 - 58) -> https://de.wikipedia.org/wiki/DCF77
// Paritätsbit
// Zu Minute, Stunde und Datum gibt es jeweils ein Paritätsbit (gerade Parität (https://de.wikipedia.org/wiki/Parit%C3%A4tsbit)).
// Das Paritätsbit hilft die Uhrzeit einigermassen zu verifizieren, aber eine Garantie ist es nicht.
// Ich würde, wenn man es etwas zuverlässiger wissen möchte, für einen abgeschlossenen Datenempfang jeweils 3 komplette Datensätze miteinander abgleichen.
// Dann kann man eher beurteilen, wenn sich die Uhrzeit in einem 10-Minuten-Bereich liegt, dass sie wohl stimmen sollte. Das habe ich aber im Minimalskript hier unterlassen.
//
//
// Hinweis zum Code:
// Programmtechnisch wird sich sicherlich noch einiges optimieren lassen, es geht aber ja mehr darum mal ein Bild zu machen.

unsigned long SignalstartMillis; // Wird benötigt um eruieren zu können, wie lange das Signal LOW resp. HIGH war.
bool ZeitBeziehen = false; // Am Pin 4 ist ein Schalter angehängt, welchen man um zu schalten auf LOW ziehen muss, vorher beginnt er nicht mit dem Zeit beziehen.
unsigned short SignaleingangStand = 0; // Ist der Zähler für das Array "SignalWerte"
unsigned short SignalPin = 2; // Digitalpin, muss aber an einem Interrupt-Fähigen GPIO-Pin hangen.
unsigned short SchalterPin = 4; // Ist INPUT_PULLUP, muss auf LOW geschaltet werden, damit das Skript anfängt die Zeit zu beziehen.
// Paritäten (gerade Parität) benötigen wir um die Zeit auf Korrektheit zu verifizieren:
unsigned short ParitaetMinute = 0;
unsigned short ParitaetStunde = 0;
unsigned short ParitaetDatum = 0;
unsigned int Signaldauer = 0; // Wird benötigt um eruieren zu können, wie lange das Signal LOW resp. HIGH war.
int SignalWerte[59]; // In dieses Array werden die Sekunden-Bits geladen.
int StatusSignalAktuell = 0; // steht für den aktuellen Wert am SignalPin.
int Vorher = 5; // Wird benötigt um Signaländerungen am Interrupt zu erkennen und darauf zu reagieren (nach jedem LOW kommt ein HIGH während einer Sekunde...).
bool TelegrammendeErreicht = false; //Diese Variable habe ich nicht verwendet, man könnte aber damit entsprechend etwas machen...

void setup() {
  Serial.begin(115200); // Für Debugzwecke, die Zeit wird man sich ja danach in ein Zeitmodul über I2C oder ähnlich schreiben.
  pinMode(SignalPin, INPUT);
  pinMode(SchalterPin, INPUT_PULLUP);
  SignalstartMillis = millis();
}

void loop() {
  if(digitalRead(SchalterPin) == LOW && ZeitBeziehen == false)
  {
    // Wird nur gestartet, wenn PIN 4 auf LOW ist.
    Serial.println("GO"); // Zur Information
    SignaleingangStand = 0;
    ParitaetMinute = 0;
    ParitaetStunde = 0;
    ParitaetDatum = 0;
    attachInterrupt(digitalPinToInterrupt(SignalPin), meldeStatus, CHANGE); // Muss auf CHANGE stehen, wir müssen beide Stati (LOW und HIGH) lesen.
    ZeitBeziehen = true;
  }
  if(ZeitBeziehen == true)
  {
    if(Vorher == 5)
    {
      //Initialisiert den Startwert.
      Vorher = StatusSignalAktuell;
      SignalstartMillis = millis();
    }
    if(StatusSignalAktuell != Vorher)
    {
     
      int Signaldauer = millis()-SignalstartMillis;
     
      if(Vorher == 0)
      {
        //0 Heisst es ist eine Information, weil Low -> 100ms Low = 0, 200ms low = 1
        if(Signaldauer > 30)
        {
          if(Signaldauer < 191)
          {
            //Gilt als 0
            Serial.print(SignaleingangStand);
            Serial.println(" : 0");
            SignalWerte[SignaleingangStand]=0;
          }
          if(Signaldauer > 190 && Signaldauer < 500 )
          {
            //Gilt als 1
            Serial.print(SignaleingangStand);
            Serial.println(" : 1");
            SignalWerte[SignaleingangStand]=1;
            //Parität aufrechnen:
            if(SignaleingangStand > 20 && SignaleingangStand < 28)
            {
              ParitaetMinute++;
            }    
            if(SignaleingangStand > 28 && SignaleingangStand < 35)
            {
              ParitaetStunde++;
            }  
            if(SignaleingangStand > 35 && SignaleingangStand < 58)
            {
              ParitaetDatum++;
            }      
          }
          SignaleingangStand++;
        }
      }
      else
      {
       
       
        if(Signaldauer > 1500)
        {
          Serial.println("-----Telegrammende-----");
          Serial.println(SignaleingangStand);
          TelegrammendeErreicht = true;

          bool ParitaetKorrekt = true;
          String DebugInfos = "";
          DebugInfos = ParitaetMinute;
          DebugInfos += " - ";
          DebugInfos += (SignalWerte[28]);
          DebugInfos += "ParitätMinute\n";
          if(ParitaetMinute %2 == 0)
          {
            ParitaetMinute = 0;
          }
          else
          {
            ParitaetMinute = 1;
          }
          DebugInfos +=  ParitaetStunde;
          DebugInfos += " - ";
          DebugInfos += SignalWerte[35];
          DebugInfos += "ParitätStunde\n";
          if(ParitaetStunde %2 == 0)
          {
            ParitaetStunde = 0;
          }
          else
          {
            ParitaetStunde = 1;
          }
          DebugInfos += ParitaetDatum;
          DebugInfos += " - ";
          DebugInfos += SignalWerte[58];
          DebugInfos += "ParitätDatum\n";
          if(ParitaetDatum %2 == 0)
          {
            ParitaetDatum = 0;
          }
          else
          {
            ParitaetDatum = 1;
          }
          Serial.println(DebugInfos);
          if(ParitaetMinute != SignalWerte[28])
          {
            ParitaetKorrekt = false;  
          }
          if(ParitaetStunde != SignalWerte[35])
          {
            ParitaetKorrekt = false;  
          }
          if(ParitaetDatum != SignalWerte[58])
          {
            ParitaetKorrekt = false;  
          }
          //Nun das Array SignalWerte durchgehen für die Zeit:
          if(ParitaetKorrekt == true)
          {
            ZeitAusgeben();
          }
          Serial.println("<----ENDE DURCHGANG---->");
          SignaleingangStand = 0;
          ParitaetMinute = 0;
          ParitaetStunde = 0;
          ParitaetDatum = 0;
        }
      }
      //Variablen zurücksetzen:
      SignalstartMillis = millis();
      Vorher = StatusSignalAktuell;
    }
    if(SignaleingangStand>59){
      SignaleingangStand = 0;
      ParitaetMinute = 0;
      ParitaetStunde = 0;
      ParitaetDatum = 0;
    }
 
  }
}

void meldeStatus() {
    StatusSignalAktuell = digitalRead(SignalPin); //Nur Variable aktualisieren, hier kein Serial.print usw...!
}


void ZeitAusgeben() {
            //Diese Funktion zählt anhand der Tabelle von Wikipedia einfach die Werte zusammen.
            //Hier würde man die Werte dann in ein Zeitmodul schreiben.
            int Minute = 0;
            int Stunde = 0;
            int Tag = 0;
            int Monat = 0;
            int Jahr = 0;
            if(SignalWerte[21] == 1)
            {
              Minute = Minute+1;
            }
            if(SignalWerte[22] == 1)
            {
              Minute = Minute+2;
            }
            if(SignalWerte[23] == 1)
            {
              Minute = Minute+4;
            }
            if(SignalWerte[24] == 1)
            {
              Minute = Minute+8;
            }
            if(SignalWerte[25] == 1)
            {
              Minute = Minute+10;
            }
            if(SignalWerte[26] == 1)
            {
              Minute = Minute+20;
            }
            if(SignalWerte[27] == 1)
            {
              Minute = Minute+40;
            }
            if(SignalWerte[29] == 1)
            {
              Stunde = Stunde+1;
            }
            if(SignalWerte[30] == 1)
            {
              Stunde = Stunde+2;
            }
            if(SignalWerte[31] == 1)
            {
              Stunde = Stunde+4;
            }
            if(SignalWerte[32] == 1)
            {
              Stunde = Stunde+8;
            }
            if(SignalWerte[33] == 1)
            {
              Stunde = Stunde+10;
            }
            if(SignalWerte[34] == 1)
            {
              Stunde = Stunde+20;
            }
            if(SignalWerte[36] == 1)
            {
              Tag = Tag+1;
            }
            if(SignalWerte[37] == 1)
            {
              Tag = Tag+2;
            }
            if(SignalWerte[38] == 1)
            {
              Tag = Tag+4;
            }
            if(SignalWerte[39] == 1)
            {
              Tag = Tag+8;
            }
            if(SignalWerte[40] == 1)
            {
              Tag = Tag+10;
            }
            if(SignalWerte[41] == 1)
            {
              Tag = Tag+20;
            }
            if(SignalWerte[45] == 1)
            {
              Monat = Monat+1;
            }
            if(SignalWerte[46] == 1)
            {
              Monat = Monat+2;
            }
            if(SignalWerte[47] == 1)
            {
              Monat = Monat+4;
            }
            if(SignalWerte[48] == 1)
            {
              Monat = Monat+8;
            }
            if(SignalWerte[49] == 1)
            {
              Monat = Monat+10;
            }
            if(SignalWerte[50] == 1)
            {
              Jahr = Jahr+1;
            }
            if(SignalWerte[51] == 1)
            {
              Jahr = Jahr+2;
            }
            if(SignalWerte[52] == 1)
            {
              Jahr = Jahr+4;
            }
            if(SignalWerte[53] == 1)
            {
              Jahr = Jahr+8;
            }
            if(SignalWerte[54] == 1)
            {
              Jahr = Jahr+10;
            }
            if(SignalWerte[55] == 1)
            {
              Jahr = Jahr+20;
            }
            if(SignalWerte[56] == 1)
            {
              Jahr = Jahr+40;
            }
            if(SignalWerte[57] == 1)
            {
              Jahr = Jahr+80;
            }
            Serial.print(Tag);
            Serial.print(".");
            Serial.print(Monat);
            Serial.print(".20");
            Serial.print(Jahr);
            Serial.print(" ");
            Serial.print(Stunde);
            Serial.print(":");
            Serial.println(Minute);
}



Sketch herunterladen: DCF77_Simpel_Zeitsync.zip