Raspberry Pi und DCF77 Empfänger von Conrad

Gerade habe ich einen Raspberry Pi zur Funkuhr aufgerüstet. Das Funksignal kommt vom DCF77 – das ist der Sender, den auch eure normalen Funkuhren benutzen.

(Hinweis: Das war damals ein Raspberry Pi 1 oder 1+. Ich weiß leider nicht, ob das mit den aktuellen Generationen noch genau so funktioniert. Ich bin sehr dankbar, wenn Ihr Eure Erfahrungen mit aktuellen Raspberrys in den Kommentaren teilt!)

Conrad DCF77 Platine

Als erstes besorgt ihr euch die DCF77 Platine von Conrad: http://www.conrad.de/ce/de/product/641138/Conrad-DCF-Empfaengerplatine

Verkabelung

Sobald ihr die habt, beschriftet am besten gleich die Kontakte mit ihren Nummern:

  • 1: Masse / GND
  • 2: Betriebsspannung / VCC
  • 3: DCF Ausgang, normal
  • 4: DCF Ausgang, invertiert

DCF77 Platine Beschriftung

Kontakt 3 werden wir überhaupt nicht benutzen.

Zwischen Kontakt 2 und Kontakt 4 klemmt ihr einen Widerstand. Ich habe einen mit circa 6 kOhm benutzt.

Aus den Kontakten 1, 2 und 4 legt ihr jetzt jeweils ein Kabel heraus. Das Ende der Leitung ist am besten eine Steckverbindung, die man später einfach auf einen Pin draufstecken kann. Tut euch selber einen Gefallen, und verwendet (im Unterschied zu mir) drei verschiedenfarbige Kabel.

Ausrichtung

Bei der Platine ist eine Antenne dabei. Das ist dieses fette graue Ding aus Ferrit. Damit die Antenne den besten Empfang bekommt, richtet ihr sie „gegensätzlich“ zum Sender aus. Der Sender steht bei Frankfurt am Main (konkreter: Mainflingen).

Schaut am einfachsten bei Google Maps nach, wo ihr seid und wo Mainflingen ist: https://www.google.de/maps/place/Mainflingen,+Mainhausen/@50.3750485,9.3278463,7z/data=!4m2!3m1!1s0x47bd3f059ce2631b:0xa224352a99a4560

Schaut dann grob, was die Himmelsrichtung ist (von mir aus liegt Mainflingen in Richtung Nord-Nord-Ost). Nehmt dann einfach euer Smartphone zur Hand, schaut auf die Compass App und sucht wo die Himmelsrichtung ist. Wenn ihr die habt, dann muss die Antenne einfach 90° dazu gedreht werden. Außerdem sollte die Antenne einfach horizontal liegen und nicht etwa stehen. Achtet zudem darauf, dass die Antenne ausreichend weit von technischem Gerät entfernt ist. Ganz schlecht ist beispielsweise, die Antenne auf den Raspberry Pi zu legen oder in ein gemeinsames Gehäuse zu verbauen. (Die Chips erzeugen hochfrequente Wellen, die dann direkt wieder in eure Antenne gehen und das Signal komplett unbrauchbar machen.)

Raspberry Pi bzw. NTPd

Ich gehe mal davon aus, dass ihr den RPi schon eingerichtet habt. In meinem Fall habe ich die Raspbian Distribution verwendet. Bei einer anderen funktionieren die Dinge vielleicht etwas anders.

Verkabeln

Die Kabel die von der Platine kommen müssen natürlich noch an den RPi gesteckt werden. Eine Übersicht der Pins im Allgemeinen gibt es hier:

Raspberry Pi Pins und Belegung

(Die Grafik ist von http://developer-blog.net/hardware/raspberry-pi-gpio-schnittstelle-teil-1/, dem ewiger Dank und Ehre gebührt.)

Die Pins werden nun wie folgt verbunden (links die DCF77 Platine mit den Bezeichnern von oben, rechts der RPi mit den Bezeichnern der Grafik):

  • 1 (GND) → 9 („Ground“)
  • 2 (VCC) → 1 („3V3“)
  • 3 bleibt leer
  • 4 (DCF) → 10 (GPIO15 RXD)

DCF77 Platine

Serielle Konsole frei machen

Die Signale kommen später auf der seriellen Konsole an. Da das Linux standardmäßig diese Konsole für etwas anderes verwendet, müssen wir die erst noch frei machen. Dazu bearbeiten wir zuerst die Datei /boot/cmdline.txt. Standardmäßig stehen dort einige Parameter mit ttyAMA0 drin. Diese entfernen wir. Danach sah die Datei bei mir so aus:

wc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

Die nächste Datei ist /etc/inittab. Dort kommentieren wir ebenfalls die Zeile zu ttyAMA0 aus:

#Spawn a getty on Raspberry Pi serial line
#T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

NTP einrichten

NTP beim if-up verhindern

Standardmäßig wird die Zeit von einem NTP Server geholt, wenn Raspbian eine Netzwerkverbindung bekommt. Das können wir in /etc/network/if-up.d/ntpdate abstellen. Da kann man als zweite Zeile einfach folgendes einfügen:

exit 0

Man kann die Datei stattdessen aber vermutlich auch einfach löschen.

Symlink setzen

Die Daten vom DCF77 Empfänger kommen über /dev/ttyAMA0 rein. Der ntpd wird die Daten aber auf /dev/refclock-0 erwarten. Also legen wir einfach einen Symlink. Da /dev ein tmpfs ist, muss der Link bei jedem Start neu erstellt werden. Das können wir einfach in /etc/init.d/ntp machen:

if [ ! -L /dev/refclock-0 ]; then
        ln -s /dev/ttyAMA0 /dev/refclock-0
fi

## diese vorhandenen Zeilen auskommentieren
#if [ -e /var/lib/ntp/ntp.conf.dhcp ]; then
#       NTPD_OPTS="$NTPD_OPTS -c /var/lib/ntp/ntp.conf.dhcp"
#fi

Zusätzlich haben wir damit noch ntpd gesagt, dass er sich nicht weiter um andere NTP-Server kümmern soll, über die wir evtl. von unserem Router informiert wurden.

NTPd einrichten

Die /etc/ntp.conf sollte so aussehen:

driftfile /var/lib/ntp/ntp.drift

# Enable this if you want statistics to be logged.
logfile /var/log/ntpd
logconfig =all
statsdir /var/log/ntpstats/

statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable

# By default, exchange time with everybody, but don't allow configuration.
restrict -4 default kod notrap nomodify nopeer noquery
restrict -6 default kod notrap nomodify nopeer noquery

# Local users may interrogate the ntp server more closely.
restrict 127.0.0.1
restrict ::1

# nicht zwingend notwendig, nur für spätere Berechnung der fudgetime
server ptbtime1.ptb.de noselect
server ptbtime2.ptb.de noselect
server ptbtime3.ptb.de noselect

# der eigentliche Zeit"server", d.h. unser DCF77-Empfänger
server 127.127.8.0 mode 5

# diese Zeile beachten wir erst später, und dient zur Korrektur der Signallaufzeit
fudge 127.127.8.0 time1 YOUR_CALCULATION

Besonders wichtig ist die letzte Zeile. Diese etwas merkwürdige IP Adresse weist den ntpd an, den DCF77 Empfänger zu benutzen. Was man dabei wirklich wissen sollte, ist dass die 0 bedeutet, dass /dev/refclock-0 verwendet werden soll. Die 8 und das mode 5 sind auch wichtig, aber für’s Verständnis bzw. nachbauen egal.

Logging und Permissions

Dann gehen wir noch in /var/log und legen eine Datei ntpd an:

sudo touch ntpd && sudo chown ntp:ntp ntpd

Da der ntpd unter dem User „ntp“ läuft, muss dieser noch Zugriff auf die ttyAMA0 bzw refclock-0 bekommen:

sudo adduser ntp tty ; sudo adduser ntp dialup

(Meiner Meinung muss es die Gruppe dialup sein, aber einige Leute meinen, es sei tty. Daran soll es aber nicht scheitern: deshalb einfach beide.)

Logging Beispiele

Nachfolgend einige Meldungen aus dem ntpd-Log, und was sie bedeuten:

# vom Signal sind nur 7 bits angekommen; danach war die Verbindung gestört
31 Jul 00:07:00 ntpd[2533]: parse: convert_rawdcf: INCOMPLETE DATA - time code only has 7 bits

# alle Bits sind angekommen, aber der Paritäts-Check ist fehlgeschlagen
30 Jul 23:35:00 ntpd[2396]: parse: convert_rawdcf: parity check FAILED for "#-######-#--#---A--LS-2-81--P1---12p---8121-412----2-8---8"

# die Uhr hat sich erfolgreich synchronisiert und ntpd stellt seine Dienste im Netz bereit
31 Jul 00:08:00 ntpd[2533]: 0.0.0.0 c215 05 clock_sync

Fertig

Jetzt noch neustarten, und das sollte es dann auch gewesen sein.

Falls es nicht funktioniert, ist das natürlich schade. Unter /var/log/ntpd gibt es evtl. weitere Anhaltspunkte. Ebenso gibt ein „ntpq -c as -c cv -c rv“ mitunter sinnvolle Informationen.

Sollte etwas grundlegend nicht funktionieren, ist man ohne Elektrotechnik-Kenntnisse und -Bastelkiste vermutlich aufgeworfen.

Software-seitig gibt es dieses sehr hilfreiche Tool: https://github.com/rene0/dcf77pi Damit kann man die Daten des Empfängers auslesen. Die DCF-Verbindung sollte dazu auf Pin 11 (GPIO17) gelegt werden. Zusätzlich muss man eventuell in der Config (wenn ich mich recht erinnere) „activehigh = 0“ setzen

Fudgetime korrigieren

Wenn man NTP übers Internet benutzt, dann kann er die Latenz recht gut messen und herausrechnen. Bei Funk geht das aber nicht, da er nicht weß wie lange das Signal denn nun zum Verarbeiten gebraucht hat. Deswegen kann man einmal am Anfang einen Internet-NTP Server als Referenz nehmen, um das zu berechnen.

Kopiert euch dafür dieses schnell von mir zusammengefrickelte Skript und führt es aus:

echo "This script will calculate the mean offset if you provided some noselect ntp-servers in your ntp.conf"

echo "If you did not provide any noselect ntp servers, this script will fail. It will also fail if those servers are not reachable."

echo
echo "Your current fudge time is:"
ntpq -c cv | grep fudgetime1 | cut -d" " -f 5

echo
echo "The mean offset (compared to the 'noselect' ntp-servers in your ntp.conf) is:"
cat /var/log/ntpstats/peerstats | grep -v 127.127.8 | cut -d" " -f 5 | awk '{a+=$1} END{print a/NR}'

echo
echo "Add up those values, divide them by 1000, and add this line to your ntp.conf:"
echo "fudge 127.127.8.0 time1 YOUR_CALCULATION"

In der Ausgabe sollte so etwas stehen wie, dass die aktuelle Fudgetime (fest einprogrammiert) 292ms ist, und der gemessene Offste ist zusätzlich vermutlich circa 640ms. Das rechnet ihr zusammen, dividert es durch 1000 und tragt dann also bspw. das hier in die ntp.conf ein:

fudge 127.127.8.0 time1 0.932

automatischer Restart bei Panic Stops

Manchmal, wenn auch tendentiell selten, kommt es vor, dass der Sender eine falsche Uhrzeit empfängt. Meist wird das durch Parity-Bits verhindert – aber manchmal schlägt das fehl. Wenn die Zeit dann mehr als 1000 Sekunden von der aktuellen abweicht, dann beendet sich ntpd. Im Falle von NTP mag das sinnvoll sein, aber bei einer DCF77-Funkuhr ist das eher kontraproduktiv.

Deswegen richten wir ein Monitoring ein, das einfach ntpd neustartet sobald er sich beendet hat. Dazu installieren wir zuerst das Paket „monit“ und richten dann die Datei /etc/monit/conf.d/ntpd ein:

check process ntpd with pidfile /var/run/ntpd.pid
group ntpd
start program = "/etc/init.d/ntp start"
stop program = "/etc/init.d/ntp stop"

9 Gedanken zu „Raspberry Pi und DCF77 Empfänger von Conrad“

  1. Hi,
    tolles Projekt! Habe ich nachgebaut, aber scheint ein Problem zu geben, welches ich nicht herausfinde.

    Kannst du mir vielleicht helfen und hast ein Idee? Ich nutze im Gegensatz zur dir einen RaPi B+


    # ntpq -c as -c cv -c rv

    ind assid status conf reach auth condition last_event cnt
    ===========================================================
    1 45447 8011 yes no none reject mobilize 1
    associd=0 status=0000 , no events, clk_unspec,
    device="RAW DCF77 CODE (Conrad DCF77 receiver module)", timecode=,
    poll=236, noreply=0, badformat=0, baddata=0, fudgetime1=3903.150,
    stratum=1, refid=DCFa, flags=0, refclock_time="",
    refclock_status="", refclock_format="RAW DCF77 Timecode",
    refclock_states="*NOMINAL: 04:11:06 (100.00%); running time: 04:11:06"
    associd=0 status=c012 leap_alarm, sync_unspec, 1 event, freq_set,
    version="ntpd 4.2.6p5@1.2349-o Mon Feb 9 03:34:42 UTC 2015 (1)",
    processor="armv6l", system="Linux/3.18.7+", leap=11, stratum=16,
    precision=-20, rootdelay=0.000, rootdisp=225.990, refid=INIT,
    reftime=00000000.00000000 Thu, Feb 7 2036 7:28:16.000,
    clock=d899aafa.f0a1db63 Thu, Feb 26 2015 15:23:54.939, peer=0, tc=3,
    mintc=3, offset=0.000, frequency=-12.837, sys_jitter=0.000,
    clk_jitter=0.001, clk_wander=0.000

    Das Loglevel habe ich auch erhöht und im im ntpd Log steht das hier, nach dem Start, aber mehr kommt dann auch nicht.


    26 Feb 11:12:48 ntpd[3655]: NTP PARSE support: Copyright (c) 1989-2009, Frank Kardel
    26 Feb 11:12:48 ntpd[3655]: PARSE receiver #0: reference clock "RAW DCF77 CODE (Conrad DCF77 receiver module)" (I/O device /dev/refclock-0, PPS device /dev/refclock-0) added
    26 Feb 11:12:48 ntpd[3655]: PARSE receiver #0: Stratum 0, trust time 00:00:00, precision -20
    26 Feb 11:12:48 ntpd[3655]: PARSE receiver #0: rootdelay 0.000000 s, phase adjustment 0.292000 s, PPS phase adjustment 0.000000 s, normal IO handling
    26 Feb 11:12:48 ntpd[3655]: PARSE receiver #0: Format recognition: RAW DCF77 Timecode
    26 Feb 11:12:48 ntpd[3655]: PARSE receiver #0: NO PPS support
    26 Feb 11:12:48 ntpd[3655]: GENERIC(0) 8011 81 mobilize assoc 45447
    26 Feb 11:12:48 ntpd[3655]: PARSE receiver #0: new phase adjustment 3.903150 s
    26 Feb 11:12:48 ntpd[3655]: 0.0.0.0 c016 06 restart
    26 Feb 11:12:48 ntpd[3655]: 0.0.0.0 c012 02 freq_set kernel -12.837 PPM

    Wie man hier sieht, ist reach auf 0, was wohl bedeuten soll, dass er den Empfänger nicht erreicht


    # ntpq -p
    remote refid st t when poll reach delay offset jitter
    ==============================================================================
    GENERIC(0) .DCFa. 1 l - 64 0 0.000 0.000 0.000

    Wenn ich von einem anderen Linux System ntpdate ausführe, bekomme ich diese Fehlermeldung


    # ntpdate -vd pi
    26 Feb 18:04:53 ntpdate[15893]: ntpdate 4.2.6p5@1.2349-o Sat Dec 20 18:47:36 UTC 2014 (1)
    transmit(192.168.178.41)
    receive(192.168.178.41)
    transmit(192.168.178.41)
    receive(192.168.178.41)
    transmit(192.168.178.41)
    receive(192.168.178.41)
    transmit(192.168.178.41)
    receive(192.168.178.41)
    192.168.178.41: Server dropped: strata too high
    server 192.168.178.41, port 123
    stratum 16, precision -20, leap 11, trust 000
    refid [192.168.178.41], delay 0.02617, dispersion 0.00000
    transmitted 4, in filter 4
    reference time: 00000000.00000000 Thu, Feb 7 2036 7:28:16.000
    originate timestamp: d899abe5.3419c93a Thu, Feb 26 2015 15:27:49.203
    transmit timestamp: d899d0bc.11614720 Thu, Feb 26 2015 18:05:00.067
    filter delay: 0.02620 0.02620 0.02617 0.02618
    0.00000 0.00000 0.00000 0.00000
    filter offset: -9430.86 -9430.86 -9430.86 -9430.86
    0.000000 0.000000 0.000000 0.000000
    delay 0.02617, dispersion 0.00000
    offset -9430.864957

    26 Feb 18:05:00 ntpdate[15893]: no server suitable for synchronization found

    Wenn du eine Idee hättest, wäre das echt super! Danke sehr schon im vorher.

    1. Hi,
      leider habe ich das Projekt damals in Auftrag zusammengebaut und nun nicht mehr in Reichweite.
      Es gibt neben ntpd aber noch weitere Tools, die mit dem Signal umgehen können: z.B. das hier https://github.com/rene0/dcf77pi und das http://www.comif.de/rpi-dcf77-interface

      Ansonsten kann ich dir eigentlich nur empfehlen, mal in einem RPi-Forum nachzufragen. Das Projekt ist leider zu lange her, als dass ich noch aus dem Kopf heraus irgendetwas sinnvolles beisteuern könnte. :)
      Du kannst höchstens mal noch sicherstellen, dass die Antenne weit genug von anderen Geräten entfernt ist und dann die obige Softwares ausprobieren.

      Viel Erfolg und Spaß!

  2. Hallo, ich möchte genau das gleiche zusammenbauen. Daher danke schonmal für die Anleitung! Ich verstehe aber nicht ganz wozu der Widerstand zwischen VCC und DCF_inv benötigt wird? Ich würde mich da über eine kurze Erklärung freuen. Danke, Martin

    1. Hi Martin,
      ich selber bin in Elektrotechnik leider ziemlich unbewandert – das stand so auf dem beiliegenden Zettel der Platine ;-). Vielleicht kann dir in einem E-Technik-Forum jemand qualifizierter helfen.
      Viele Grüße!

      1. Hallo zusammen,
        es ist ganz einfach:
        der DCF77-Empfänger hat einen Open-Collector-Ausgang d.h. dieser schaltet gegen Masse (GND). Würde der Widerstand nicht vorhanden sein hätten der Ausgang einen nicht definierten Zustand oder halt 0V. Der definierte Zustand von 3,3V wird durch den Widerstand festgelegt. Der Widerstand wird auch „Pullup“ bezeichnet.

  3. Hallo,

    hat jemand dieses Projekt aktuell an einem Pi2 oder Pi3 funktionstüchtig zum laufen gebracht?

    Habe schon vieles probiert, aber eine Zeit wurde bisher nie geliefert.
    Das Modul ist zumindest in Ordnung, da es seriell angeschlossen an einem PC die Uhrzeit liefert.

    1. Hallo Ricky,

      Da ich mich mit DCF77 bereits Jahre beschäftige und mit RS232 nie glücklich wurde, habe ich für den RaspberryPi selbst ein C-Programm geschrieben, das das Signal des Empfängers direkt an einem GPIO auswertet.
      Download des Sourcecode: http://lepanto.at/download/dcf77_clock.c
      Kompilieren mit:
      $ gcc -Wall -pedantic -std=c99 -lrt -lwiringPi -o dcf77_clock dcf77_clock.c

      Wie du siehst, braucht man dazu die Library ‚wiringPi‘, die aber schon im Raspian enthalten ist.
      # aptitude install wiringpi
      Die Library ‚rt‘ (steht für ‚realtime‘) ist im Basissystem schon installiert.

      Mit dem Parameter -h erhält man eine kurze Hilfe.
      $ ./dcf77_clock -h
      Usage: ./dcf77_clock [-h] [-D] -g [-g ] [-u ] [-f ] [-t ]
      -h this helptext
      -D debbuging (don’t fork to background)
      -g GPIO-pin (or pins) that is connection to the receiver
      -u unit-number of NTP shared memory driver
      -f fifoname to send additional data (bit 1 to 14)
      -t tolerance in milliseconds (default: 25)

      Der Parameter ‚-g ‚ muß einmal vorhanden sein, kann auch ein
      zweites mal vorkommen. Damit wird der GPIO-Pin angegeben auf dem das
      Signal anliegt. Wenn man beim Conrad-Empfänger beide Signale an den
      RaspberryPi anklemmt, kann man den Parameter zweimal angeben.
      Das Programm verwendet die WiringPi-Nummerierung.
      –> https://pinout.xyz/pinout/wiringpi

      Der Parameter ‚-u ‚ ist die ‚Shared Memory Unit‘ die das Programm
      verwenden soll, um den NTPD die Zeitdaten zu übergeben.
      In der ntp.conf wird das mit den folgenden Zeilen eingerichtet:

      server 127.127.28.0 minpoll 6 maxpoll 6
      fudge 127.127.28.0 time1 0.030 refid DCF

      Der ‚time1‘ Parameter liegt bei mir etwa bei 0.030. Du kannst den auch weglassen und selbst messen welchen Versatz dein Empfänger hat. Anleitungen wie man den Versatz eines Empfängers herausfindet, gibt es genug im Netz.
      Wichtig ist hier bei der Pseudo-IP (127.127.28.x / die 28 steht für Shared Memory) die letzte Nummer. Das ist die Shared Memory Unit.
      Die Unit 0 und 1 können nur von root beschrieben und gelesen werden.
      Alle weiteren auch von unprivilegierten Usern.
      Leider muß das Programm als root laufen, da es sonst keinen Zugriff auf
      die GPIOs bekommt, daher kann man auch Unit 0 und/oder 1 verwenden.

      Der Parameter ‚-t ‚ legt die Toleranz fest, die die Flanken
      aufweisen dürfen.
      Das DCF77-Signal fällt exakt bei einer Sekunde auf 15% Signalstärke ab
      (fallende Flanke). Nach 100ms (Bitwert: 0) bzw. 200ms (Bitwert: 1)
      steigt es wieder auf 100% an (steigende Flanke).
      Durch äußere Einflüsse oder etwas schlechtem Empfang weichen die Zeiten
      etwas ab, meistens sind sie kürzer. Die Toleranz gibt an um wieviel
      Millisekunden die Flanken versetzt sein dürfen, damit es noch als gültig
      gewertet wird.

      Mit dem Parameter ‚-f ‚ kann man eine FIFO (named pipe) angeben,
      an die die extra Daten übergeben werden, wenn man diese Auswerten will.
      Seit Jahren werden in diesen Extrabits Wettervorhersagen von der Firma
      MeteoTime übertragen. Diese Daten sind verschlüsselt und man braucht
      offiziell einen Microchip der Firma HKW um diese zu entschlüsseln.
      (Damit habe ich mich vor 2 Jahren beschäftigt)
      Es gab auch vor Jahren einen Versuch Katastrophenalarm darüber
      umzusetzen. Ob das nun aktiv betrieben wird, weiss ich leider nicht und
      auch das Protocol wurde nie öffendlich bis ins Detail beschrieben.

      Übrigens:
      Bei allen Schaltungen im Netz (wie auch hier) wird ein PullUp-Widerstand verwendet.
      Das ist bei diesem Programm nicht erforderlich, da es die eingebauten
      PullUp-Widerstände vom RaspberryPi verwendet.

      Wichtig:
      Da die GPIOs nur bis 3V3 spannungsfest sind, sollte man den Empfänger nur mit 3V3 versorgen.
      Die meisten Empfänger arbeiten aber schon ab 1V2 bzw. 2V5, also ist das kein Problem.
      Bei mir laufen aktuell 3 Empfänger völlig ohne Probleme an einem RaspberryPi (für jeden Empfänger ein eigener GPIO und Prozess, und jeder Prozess mit einer anderen Unit).

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *