Benutzer-Werkzeuge

Webseiten-Werkzeuge


raspberry:dcf77_modul

Mit dem Raspberry ein DCF 77 Modul abfragen

Ziel:

Ein DCF77 Modul zum Empfang der Zeitinformation über 77,5 kHz an einem Raspberry über GPIO anschließen und über ein Python Skript auswerten.

Alternativer Anschluss

Über eine Com Schnittstelle ginge das natürlich auch mit nativen Methoden (ntp Server) und dann entsprechend einfacher, GPIO Anschluss gewählt um das Protokoll besser zu verstehen.

Siehe Materialsammlung bzgl. Anschluss über die COM Schnittstelle dazu am Ende des Artikels.

Grundlagen

Der Zeitzeichensender DCF77 ist ein Langwellensender in der Nähe von Frankfurt und versorgt Deutschland mit der geltenden gesetzlichen Uhrzeit.

Details siehe hier : http://de.wikipedia.org/wiki/DCF77

Der Sender arbeite auf einer Frequenz 77,5 kHz

Der Zeitcode baut sich so auf: http://www.ptb.de/cms/fachabteilungen/abt4/fb-44/ag-442/verbreitung-der-gesetzlichen-zeit/dcf77/zeitcode.html

Die Sekundenmarken mit einer Dauer von 0,1 s entsprechen der binären Null, die mit einer Dauer von 0,2 s der binären Eins. Die 59 Sekunde bleibt aus, die 0 Sekunde ist immer ein „LOW“.

In den ersten Bits werden Wetter Informationen versandt (siehe ⇒ http://www.meteotime.com ).


Online das DCF77 Signal abfragen

Auf dieser Seite kann das akutelle Signal decodiert beobachtet werden ⇒ http://www.dcf77logs.de/WebConsole.aspx

Ideal um das eigene Programm zu überprüfen.


Empfang mit einen fertigen DCF77 Modul

Beschrieben wird hier das Modul von ehajo, im Shop unter DCF77-Modul für 10 € erhältlich.

Das Modul besteht aus einen kleinen Platine mit einer Ferit Antenne und einen 4 Poligen Anschluss

  • VCC + 1,2 - 3,3V
  • GND -
  • SIG - Ausgangssignal - LOW wenn DCF-Signal am Maximum ist
  • Enable auf GND bzw. offen ⇒ Modul ist aktiviert

Anschluss - Übersicht:

 Anschluss des DCF77 Modul von ehajo

Nach dem Anschluss der Spannungsversorgung kann es etwas dauern bis das erste Signal angezeigt wird.

 dcf77 Signal - ersten Messung

Der Empfang in der Münchner Innenstadt funktioniert gut, am besten sollte die Antenne Rechtwinklig zur ungefähren Richtung Frankfurt betrieben werden, damit der Empfang gut klappt.

Bei einer ersten Messung fallen sofort alle LED Lampen in der Nähe negativ auf, ist die Schreibtisch Beleuchtung eingeschaltet, ist kein Empfang mehr möglich!

Der DCF77 Empfänger übernimmt die Dekodierung des Funksignals und wandelt das Signal in einen HIGH und Low Pegel um. Über einen GPIO Port wird das Modul am Raspberry angeschlossen.

 DCF77 Funk Signal Pegel zu DCF77 Modul Pegel

Zu jedem Beginn der Sekunden 0 bis 58 startet das HIGH Signal an SIG, dauert es nur 100ms wird das als eine logische 0 interpretiert, dauert das HIGH Signal 200ms wird eine logische 1 erkannt.

 DCF77 Signal Codierung 0 und 1 in der Sekunde 0 bis 58

DCF77 Signal Codierung 0 und 1 in der Sekunde 0 bis 58

Nur in der 59 Sekunde bleibt der Pegel an SIG immer auf 0 um den Anfang des Dataframes zu finden.

Umsetzung der Bits in dem Dataframe in die Zeitinformation

Sekunde 0 bis 35  Sekunde 0 bis 35

Sekunde 36 bis 59  Sekunde 36 bis 59

Übersicht
SekundeBedeutung wenn ein HIGH Pegel erkannt wird
0Start einer neuen Minute, immer LOW
1 - 14Wetterinformationen der Firma http://www.meteotime.com/de/Home/Default.htm, nicht öffentlich siehe zum Beispiel http://www.mikrocontroller.net/articles/DCF77_Wetterinformationen
15Reserveantenne bzw. Rufbit für Störungsalarmierung
16Zeitumstellung MESZ/MEZ in einer Stunde
17Z1 - Zeitzonen Bit - MEZ (Mitteleuropäische Zeit (Normalzeit/Winterzeit)) hat Z1 den Zustand LOW und Z2 den Zustand HIGH
18Z2 - Zeitzonen Bit - MESZ (Mitteleuropäische Sommerzeit (Sommerzeit)) hat Z1 den Zustand HIGH und Z2 den Zustand LOW
19Ankündigungsbit A1 - Schaltsekunde wird eingefügt (eine h zuvor)
20Telegramm beginn !Immer HIGH!
211 min
222 min
234 min
248 min
2510 min
2620 min
2740 min
28 Prüfbit für gerade Parität
291 h
302 h
314 h
328 h
3310 h
3420 h
35Prüfbit für h gerade Parität
361 Kalendertag
372 Kalendertag
384 Kalendertag
398 Kalendertag
4010 Kalendertag
4120 Kalendertag
421 Wochentag
432 Wochentag
444 Wochentag
451 Monat
462 Monat
474 Monat
488 Monat
4910 Monat
501 Jahr
512 Jahr
524 Jahr
538 Jahr
5410 Jahr
5520 Jahr
5640 Jahr
5780 Jahr
58Prüfbit für Datum gerade Parität
59Für die Synchronisation immer LOW - ??außer bei Einfügen der Schaltsekunde auf 0.1s für LOW??

Die drei Paritiy Prüfbit Bits Felder ergänzen die Code Wörter Minute,Stunde und Datum inkl. Nummer des Wochentages auf eine gerade Zahl von Einsen.

Wird zum Beispiel für die Stunde die Bitfolge 100100 empfangen, ist das Prüfbit 0, da zwei mal 1 (=gerade Anzahl) gesendet wurde. Wird 7 Uhr empfangen, Bitfolge 111000 ist das Prüfbit 1 um eine gerade Anzahl von 1 zu erzielen.

Der Wert der einzelnen Code Wörter wird im BCD Code übertragen.


Anschluss an den Raspberry

Um Schäden zu vermeiden den Raspberry herunterfahren und Spannungsversorgung trennen.

Das Modul wird über die GPIO Pins angeschlossen.

  • 3,3 V + auf Pin 1
  • Ground - auf Pin 6
  • SIG auf Pin 11 GPIO 17
  • ENABLE auf Pin 13 GPIO 27

Nach dem Neustart etwas warten und mit dem erstes einfaches Test Script überprüfen, ob bereits etwas gemessen werden kann:

testClock.py
import RPIO
import time
 
# supress warning
RPIO.setwarnings(False)
 
# Which RPIO Numbering you like to use
RPIO.setmode(RPIO.BCM)
 
# OUT - Enable Port for the module
enable_port=27
RPIO.setup(enable_port,RPIO.OUT)
RPIO.gpio_function(enable_port)
RPIO.output(enable_port,RPIO.LOW)
 
#IN - Read the data
data_port=17
RPIO.setup(data_port,RPIO.IN)
 
#endless loop
run=True
read_count=0
while run :
        #Read data
        if (RPIO.input(data_port)):
                print "Port 17 => {0:5} :: HIGH :: Count {1} ".format(str(RPIO.input(data_port)), read_count)
        else:
                print "Port 17 => {0:5} :: LOW  :: Count {1} ".format(str(RPIO.input(data_port)), read_count)
        read_count+=1
        #wait 10ms
        time.sleep(0.1)
        #stop after 100 trys
        if (read_count > 100):
                run=False
# Clean up
RPIO.cleanup()

Es ist auch nicht immer sichergestellt, das das Funksignal sauber erkannt wird, Störungen können die Auswertung der Signale erheblich stören, LED Lampen in der Nähe des Empfängers vermeiden!.

Sehr hilfreich ist hier die Online Applikation. die permanent die DFC77 Informationen der aktuellen Sekunde anzeigt. Das ist sehr hilfreich zum Debuggen des eignen Programms um zu sehen, ob der Fehler nicht sogar beim Sender bzw. aktuell in der Übertragung liegt.

Zum Beispiel wenn gar keine Signale von diesen dcf77logs Empfänger richtig erkannt werden können.

http://www.dcf77logs.de/WebConsole.aspx.

Zeitzone des Raspberry prüfen

Damit es sich einfacher Debuggen lässt, die richtige Zeitzone einstellen, falls die Uhrzeit zum Beispiel um 1 Stunde abweicht:

#Uhrzeit auslesen
date
 
# Wenn falsche Zeitzone:
 
# Alte Definition entfernen
mv /etc/localtime /tmp
 
# Neue Definition suchen 
# unter /usr/share/zoneinfo/Europe/
 
#Verlinken
ln -s /usr/share/zoneinfo/Europe/Berlin /etc/localtime

Die Signale auswerten

Die Signale können mit zwei Methoden ausgewertet werden:

  1. Abfragen des Pegels auf dem Signaleingang in einer Endlosschleife (mit 10ms Sleeps)
  2. Interrupt auf steigende Flanke setzen und damit den Beginn einer jeden Sekunde sofort erkennen

Im ersten Test habe ich eine Lösung nach 1. versucht, allerdings scheint mir eine Lösung nach 2. effizienter (Siehe Blink Code für die Signal LED=

Permanentes Pollen des Signal Einganges

Jede Sekunde innerhalb der laufenden Minute wird ein Impuls mit einer Länge von 100msec (Low) oder 200msec (High) gesendet.

In der 59-zigsten Sekunde fehlt dann dieser Impuls.

Mit dieser Synchronisations-Lücke kann der Anfang des Datagramm bestimmt werden.

Ablauf:

  • Abtasten alle x ms
  • Anfang finden
  • Sekundenweise auswerten

Dabei ist zu beachten, das eine exakte Sleep Time von 10ms auf einem Raspberry mit Python nicht erwartet werden kann.

Je nach Last und andere Faktoren kann der Wert zwischen 9ms und 11ms schwanken, daher suche ich bereits nach 950ms nach der HIGH Flanke um den Anfang des nächste Daten Bits zu finden.

So ungefähr wird das zum Schluss laufen:

59'igste Sekunden Lücke suchen
   Suche min. 1s LOW und dann die erste steigende Flanke => Anfang des Dataframes

Start die Aufzeichnung der laufenden Minute

Schleife von 0 bis 58
 Messe alle 10ms den Pegel
 Merke den Pegel=0 oder 1 (zähle diese Werte)
 Zähle die Durchläufe bis ~ 950ms (95 Durchläufe)
    Da das Signal nicht immer exakt für 1000ms abgefragt werden kann:
     Prüfe ob Pegel schon wieder auf HIGH, wenn Ja Ende (bzw. Anfang des nächsten) des Dataframes gefunden
     Wenn mehr als 20 High  => Bit Wert dieser Stelle auf Pos Record auf 1
     Wenn nach 11 schon Low => Bit Wert dieser Stelle auf Pos Record auf 0

Decodiere die gefundenen Werte:
 Je nach Pos setzen den entsprehenden Bit Wert in der jeweiligen Variable 
	 Pos  0 immer 0
	 Pos. 1 bis 14  ->  Wetter Daten
	 Pos. 15        ->  Rufbit für Alarmierung
	 Ros. 16        ->  Ankündigungsbit A1
	 Pos. 17/18     ->  Zonenzeitbits
	 Pos  19        ->  Ankündigungsbit A2
	 Pos. 20 bis 28 ->  7 Bits für die Minute + Prüfbit auf gerade Anzahl Stellen
	 Pos. 29 bis 36  -> 6 Bits für die Stunde + Prüfbit auf gerade Anzahl Stellen
	 Pos. 37 bis 59  -> 22 Bits für das Datum einschließlich der Nummer des Wochentages
	                    6 Stellen Kalender Tag
				 3 Stellen Wochentag
				 5 Stellen Kalendermonat
				 8 Stellen Kalenderjahr
				 1 prüfbit
							 
	 Pos 59 Immer 0

Werte die Ergebnisse aus  ( !BCD Kodierung -  1-2-4-8 Code) und schreibe Ergebnis in einen Logfile etc.

Ein erster Versuch inkl. der Überprüfung der Parität und setzen der OS Uhrzeit:

readClock.py
__author__ = 'gpipperr'
 
"""
Small Python Script to decode a DFC77 Module connect to the Raspberry 
 
Connect:
 SIG to Pin 11 GPIO 17
 ENABLE to Pin 13 GPIO 27
 
Parameter -d
To supress the debug output start the script with -d 01
 
Parameter -r
Define how often the time will be read
 
Paramter -e
Not anlyse in the middle of the datagramm between 300ms and 900ms to save CPU
 
Parameter -s
set the OS Clock to the last captured time
 
Example:  run 30 minutes and show no Debug Messages and not use the energy mode to save CPU and not set the OS clock
 
python readClock.py -r 30 -d  0 -e 0 -s 0
 
"""
 
import RPIO
import time
import datetime
import sys
import getopt
import exceptions
import signal
import os
 
# supress warning
RPIO.setwarnings(False)
 
# Which RPIO Numbering you like to use
RPIO.setmode(RPIO.BCM)
 
# OUT - Enable Port for the module
enable_port = 27
RPIO.setup(enable_port, RPIO.OUT)
RPIO.gpio_function(enable_port)
RPIO.output(enable_port, RPIO.LOW)
 
# IN - Read the data
data_port = 17
RPIO.setup(data_port, RPIO.IN)
 
# Globals
datagram = []
debug_level = 0
energy_mode = 0
 
# ######  define the handler if the program is stopped with ^c ######
# Clean exit!
def clean_exit():
	RPIO.cleanup()
	exit()
 
 
# define the handler if the program is stopped with ^c
def handler(signum, frame):
	print "Catch Error {} - frame :: {}".format(signum, frame)
	clean_exit()
 
 
# register the signal handler
signal.signal(signal.SIGINT, handler)
# ------------------------------------------------------------------
 
# debug info
def pdebug(text):
	if (debug_level == 1):
		print text
 
# Error Exception Handler
# If Bit20 is low
class Bit20Error(Exception):
	def __init__(self, *args, **kwargs):
		Exception.__init__(self, *args, **kwargs)
		self.args = args
		print("\n-- Error - Bit 20 is LOW - Datagram Error")
# If Bit0 is high
class Bit0Error(Exception):
	def __init__(self, *args, **kwargs):
		Exception.__init__(self, *args, **kwargs)
		self.args = args
		print("\n-- Error - Bit 0 is HIGH - Datagram Error")
# If Parity was wrong
class ParityBitError(Exception):
	def __init__(self, *args, **kwargs):
		Exception.__init__(self, *args, **kwargs)
		self.args = args
		print("\n-- Error - Parity Bit Error - Datagram Error with " + self.args[0])
 
# Check the parity bit
def checkParityBit(data_word_type, value_list, parity_bit):
	check = sum(value_list)
	pdebug("-- Parity Check for {0} : Sum {1} : Parity Bit {2}".format(data_word_type, check, parity_bit))
	# the last bit fills up the bit order to a even count
	# even
	if check % 2 == 0:
		if (parity_bit) == 1:
			raise ParityBitError("Error in :: " + data_word_type)
	else:
		if (parity_bit) == 0:
			raise ParityBitError("Error in :: " + data_word_type)
 
# find the 59 Second with 1000ms low pegel
def get59Seconde():
	print "-- Wait on 59 Second mark"
	# read the world time
	seconds = time.time()
	sleeptime = 0
	# remember low
	low_time = 0
	low_count = 0
	# remeber high
	high_time = 0
	high_count = 0
	run = True
	found59 = False
	while run:
		# Get act. time
		seconds = time.time()
		# read the port
		if (RPIO.input(data_port)):
			high_count += 1
			high_time = high_time + sleeptime
			low_count = 0
		else:
			low_count += 1
			low_time = low_time + sleeptime
			high_count = 0
		# sleep some milli seconds
		time.sleep(0.01)
		# calculate the time
		sleeptime = time.time() - seconds
		# check for the 59s - full 100 reads low
		# + the minimal low count from the 58 secound 80 ~ 175 to avoid read errors
		# if (low_time > 0.99):
		if ((low_count > 90) and (found59 == False)):
			found59 = True
		# Wait until we found the first HIGH of the 0 Second
		if ((found59) and (high_count == 1)):
			# start with the real decoding
			pdebug("-- Fond the 59second :: PC Time {0} :: High Cnt. {1:2} :: Low Cnt.  {2:2} :: Low time {3:15} :: Sleep time {4:5}".format(datetime.datetime.now().strftime("%H:%M:%S.%f"), high_count, low_count, low_time, sleeptime))
			decode = True
			run = False
	print "-- ... found"
 
# Read the datagram
def decodeMinute():
	global datagram
	global energy_mode
	akt_time = 0
	runDecode = True
	aktSecond = 0
	high_count = 0
	low_count = 0
	highDetectedafterReads = 0
	low_time = 0
	high_time = 0
	sleeptime = 0
	seconds = time.time()
	reads = 0
	while runDecode:
		seconds = time.time()
		# read
		if (RPIO.input(data_port)):
			high_count += 1
			high_time = high_time + sleeptime
			# remember after which read count of the act second the pegel was high
			if (highDetectedafterReads < 1):
				highDetectedafterReads = reads
		else:
			low_count += 1
			low_time = low_time + sleeptime
		#Energy Mode - Sleep longer in the midle of the datagramm
		if (energy_mode==1):
			if (reads==30):
				time.sleep(0.59)
				reads=90
		#Normal sleep
		time.sleep(0.01)
 
		# how long take the sleep
		sleeptime = time.time() - seconds
 
		# ms of the aktual second
		akt_time = akt_time + sleeptime
 
		# stop after 1000ms
		if (reads > 95):
			# get the next high count to found the end of the timeframe
			if (RPIO.input(data_port)):
				if (high_count > 20):
						datagram.append(1)
				else:
					datagram.append(0)
				printSecDot()
				pdebug("-- Akt Secound {0:2} :: Datagram Time {1:15} :: Reads {2:2}  :: High Cnt. {3:2}  :: Low Cnt. {4:2} :: High time {5:15} :: Low Time  {6:15}  Detect after reads {7:2}".format(aktSecond, akt_time, reads, high_count, low_count, high_time, low_time, highDetectedafterReads))
				# check for valid bits
				# if unvailed raise exception
				# First bit must be alwasy 0
				if (aktSecond == 0):
					if (datagram[0] == 1):
						raise Bit0Error
				# Bit 20 always LOW - Start of telegramm
				if (aktSecond == 20):
					if (datagram[20] == 0):
						raise Bit20Error
				# check the parity bits of the data words
				if (aktSecond == 28):
					checkParityBit("MINUTE", datagram[21:28], datagram[28])
				if (aktSecond == 35):
					checkParityBit("HOUR", datagram[29:35], datagram[35])
				if (aktSecond == 58):
					checkParityBit("CALENDAR", datagram[36:58], datagram[58])
				#set the defaults
				reads = 0
				akt_time = 0
				high_time = 0
				low_time = 0
				low_count = 0
				high_count = 0
				highDetectedafterReads = 0
				aktSecond += 1
			else:
				reads += 1
		else:
			reads += 1
		if (aktSecond > 58):
			runDecode = False
			# add 59 second
			datagram.append(0)
 
 
# get the hour
def getMinuteValue():
	global datagram
	# Check for parity errors
	# check parity bit
	checkParityBit("MINUTE", datagram[21:28], datagram[28])
	minute = datagram[21] * 1 + datagram[22] * 2 + datagram[23] * 4 + datagram[24] * 8 + datagram[25] * 10 + datagram[26] * 20 + datagram[27] * 40
	return minute
 
def getHourValue():
	global datagram
	# Check for parity errors
	# check parity bit
	checkParityBit("HOUR", datagram[29:35], datagram[35])
	hour = datagram[29] * 1 + datagram[30] * 2 + datagram[31] * 4 + datagram[32] * 8 + datagram[33] * 10 + datagram[34] * 20
	return hour
 
# get the calendar Values
def getDayValue():
	global datagram
	checkParityBit("CALENDAR", datagram[36:58], datagram[58])
	day	 = datagram[36] * 1 + datagram[37] * 2 + datagram[38] * 4 + datagram[39] * 8 + datagram[40] * 10 + datagram[41] * 20
	return day
def getWeekDayValue():
	global datagram
	checkParityBit("CALENDAR", datagram[36:58], datagram[58])
	weekday = datagram[42] * 1 + datagram[43] * 2 + datagram[44] * 4
	return weekday
def getMonthValue():
	global datagram
	checkParityBit("CALENDAR", datagram[36:58], datagram[58])
	month   = datagram[45] * 1 + datagram[46] * 2 + datagram[47] * 4 + datagram[48] * 8 + +datagram[49] * 10
	return month
def getYearValue():
	global datagram
	checkParityBit("CALENDAR", datagram[36:58], datagram[58])
	year = datagram[50] * 1 + datagram[51] * 2 + datagram[52] * 4 + datagram[53] * 8 + datagram[54] * 10 + datagram[55] * 20 + datagram[56] * 40 + datagram[57] * 80
	return year+2000
#Sommer or Wintertime MEZ or MESZ
def getTimeZoneValue():
	timezone="undef"
	if (datagram[17]==0) and (datagram[18]==1):
		timezone="MEZ"
	if (datagram[17]==1) and (datagram[18]==0):
		timezone="MESZ"
	return timezone
#get at normal date object to set the clock of the server
def getDCF77TimeStamp():
	second = 0
	microsecond = 0
	dcf77_date = datetime.datetime(getYearValue(), getMonthValue(), getDayValue(), getHourValue(), getMinuteValue(), second, microsecond)
	return dcf77_date
 
#set the OS Date to the right date
def setOSDate(set_date):
	#after the first sucess full read we can set the os clock
	if (set_date==1):
		try:
			new_os_time_str="\"{0:4d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:00\"".format(getYearValue(),getMonthValue(),getDayValue(),getHourValue(),getMinuteValue())
			print "-- Set the OS Clock with the data string {0} :: Diff {1}".format(new_os_time_str, (datetime.datetime.now()-getDCF77TimeStamp()))
			os.system("date -s " + new_os_time_str + " > /dev/null")
		except:
			print "-- Unkown error::", sys.exc_info()
			print "-- Finish to set OS time to  {0}".format(datetime.datetime.now().strftime("%H:%M:%S.%f"))
 
# print the dataframe
# for debug purpose on the same line if used in decode function
def printData():
	global datagram
	# print "\r data:: {0}".format(datagram)
	count = 1
	sys.stdout.write("\r-- Datagramm Value::")
	for val in datagram:
		sys.stdout.write(str(val))
		if count == 1:
			sys.stdout.write("-")
		if count == 15:
			sys.stdout.write("-")
		if count == 20:
			sys.stdout.write("-")
		if count == 21:
			sys.stdout.write("-")
		if count == 29:
			sys.stdout.write("-")
		if count == 36:
			sys.stdout.write("-")
		if count == 42:
			sys.stdout.write("-")
		if count == 45:
			sys.stdout.write("-")
		if count == 50:
			sys.stdout.write("-")
		if count == 59:
			sys.stdout.write("-")
		count += 1
	sys.stdout.flush()
 
 
# print a dot on the same line
def printSecDot():
	sys.stdout.write(".")
	sys.stdout.flush()
 
 
def main(argv):
	global datagram
	global debug_level
	global energy_mode
	set_date=0
	debug_level = 0
	v_runs = 1
	error_count=0
 
	try:
		opts, args = getopt.getopt(argv, "hr:d:e:s:", ["help=","runs=", "debug=","energymode=","setdate="])
	except getopt.GetoptError:
		print sys.argv[0] + " -d <debug level 0 to 1> -r <runs> -e <0|1>  -s <0|1>"
		sys.exit(2)
	for opt, arg in opts:
		if opt in ("-h", "--help"):
			print sys.argv[0] + " -d <debug level 0 | 1 > -r <runs> -e < 0 | 1 > -s < 0 | 1 >"
			sys.exit()
		elif opt in ("-r", "--runs"):
			v_runs = int(arg)
		elif opt in ("-d", "--debug"):
			debug_level = int(arg)
		elif opt in ("-e", "--energymode"):
			energy_mode = int(arg)
		elif opt in ("-s", "--setdate"):
			set_date = int(arg)
 
	print 95 * "-"
	print "Start Reading the Time Value :: Runs :: {0} :: Debug Level {1} :: Energy Mode {2} :: Set OS Clock {3} ".format(v_runs, debug_level,energy_mode,set_date)
 
	success_read = False
	#start decode the minutes
	for cyles in range(v_runs):
		#
		#Start to get the start time frame
		print 95 * "="
		print "-- Read time data :: run {0}".format(cyles + 1)
 
		#If we have an read error or start
		#if (success_read==False):
		#find the start of the datagram
		get59Seconde()
 
		#read
		try:
			print "-- Start to read the actual Minute Datagram"
			decodeMinute()
			success_read = True
			print "\n"
		except Bit0Error as error:
			pdebug("-- Bit 0 Error")
			error_count+=1
			success_read = False
		except Bit20Error as error:
			pdebug("-- Bit 20 Error")
			error_count+=1
			success_read = False
		except ParityBitError as error:
			pdebug("-- Parity error")
			error_count+=1
			success_read = False
		except:
			print "-- Unkown error::", sys.exc_info()
			success_read = False
			error_count+=1
 
		#show the datagram
		printData()
		print "\n"
 
		# get date only if read was successfull
		if (success_read):
			try:
				print "-- DCF77 Time     ::{0:02d}:{1:02d} ".format(getHourValue(), getMinuteValue())
				print "-- DCF77 Calendar ::{0:02d}.{1:02d}.{2:4d} :: Day of the week {3}".format(getDayValue(),getMonthValue(), getYearValue(), getWeekDayValue())
				print "-- DCF77 Timezone ::{0}".format(getTimeZoneValue())
				print "-- DCF77 Timestamp::{0}".format(getDCF77TimeStamp())
				print "--"
			except ParityBitError as error:
				pdebug("-- Parity error::" + error.args[0])
				error_count+=1
				success_read = False
			except:
				print "-- Unkown error::", sys.exc_info()
				error_count+=1
				success_read = False
		#Set now also the date of the server
		if (success_read):
			setOSDate(set_date)
 
		# empty list for the next read
		while len(datagram) > 0: datagram.pop()
		success_read = False
		print "--"
		print "-- Finish Decode Minute :: OS internal date {0}".format(datetime.datetime.now().strftime("%H:%M:%S.%f"))
 
 
	print 80 * "-"
	print "Finish Reading the Time Value :: Runs :: {0} :: Errors {1}".format(v_runs, error_count)
	print 80 * "-"
 
# Call Main
if __name__ == "__main__":
	main(sys.argv[1:])
 
# Clean up
clean_exit()

Nächste Schritte:

Die Beachtung der Schaltsekunde ist noch nicht realisiert.

Läuft das Programm in einer Schleife wird nur jeder zweiter Dataframe ausgewertet, da jedes mal wieder neu der Anfang gesucht werden muss, das klappt durch die Ausgaben und das Setzen der Zeit dann nicht jedes mal auch noch in der aktuellen Minute.

Aufruf und Ausgabe:  Ausgabe eines Python Scripts für die Auswertung eines DCF77 Empfängers am Raspberry

Performance Überlegungen

Der CPU Verbrauch des obigen Skripts hält sich in Grenzen, ohne weitere Last auf dem System ~ 1,6% CPU.

Aber evtl. lässt sich noch etwas einsparen, wenn auf die Abtastung von 300ms bis 900ms verzichtet wird, d.h. Wenn der Zähler auf 30 steht, 600ms schlafen danach Zähler auf 90 und weiter prüfen.

Scheint soweit auch zu funktionieren, allerdings ist der Vorteil kaum messbar.


Interrupt auf den Signal Eingange legen

Im ersten Schritt eine LED wird an einen weiteren GPIO Port angeschlossen, diese soll im Sekunden Rhythmus blinken sobald der Pegel auf HIGH geht.

Anschluss meines einfaches LED Modul mit MOSFET 2N7002ET1G und einer Jumpo LED:

  • LED Input -IN to Pin 15 GPIO 22
  • Ground - Pin 9
  • +5V - Pin 4

Hier wird auf die Interrupt Methode gesetzt:

import RPIO
import time
import signal
 
 
"""
Small Python Script to decode a DFC77 Module connect to the Raspberry 
Connects:
 DFC77 Module - SIG to Pin 11 GPIO 17
 LED Input -    IN  to Pin 15 GPIO 22
 
runs in a endless loop
 
"""
#globals
blink_count=0
run=True
call_time=time.time()
 
#define the interrupt function
def SetLed(gpio_id,val):
		global blink_count
		global call_time
		time_gap=time.time()-call_time
		if time_gap > 1.8:
			blink_count=0
			print "-- Fond First Minute "
			#here we can start to decode the bits
		else:
			blink_count+=1
		call_time=time.time()
		global signal_state
		RPIO.output(out_port,RPIO.HIGH)
		print "-- Blink on Port 22 xXx  :: Count :: {0} :: Time since last Blink {1:15}".format(blink_count,time_gap)
		time.sleep(0.2)
		RPIO.output(out_port,RPIO.LOW)
 
# Clean exit!
def clean_exit():
		global run
		RPIO.output(out_port,RPIO.LOW)
		run=False
		exit()
 
#define the handler if the program is stopped with ^c
def handler(signum, frame):
		print "Catch Error {} - frame :: {}".format(signum,frame)
		clean_exit()
 
# register the signal handler
signal.signal(signal.SIGINT, handler)
 
# supress warning
RPIO.setwarnings(False)
 
# Which RPIO Numbering you like to use
RPIO.setmode(RPIO.BCM)
 
# My RPIO PORTs
# OUT
out_port=22
RPIO.setup(out_port,RPIO.OUT)
 
# IN Port with the Signal
in_port=17
RPIO.setup(in_port,RPIO.IN)
 
# register the Interrupt on both
# RPIO.add_interrupt_callback(gpio_id=in_port,callback=SetLed, edge='both', pull_up_down=RPIO.PUD_OFF, threaded_callback=False, debounce_timeout_ms=None)
 
# register the Interrupt on only Rising 
RPIO.add_interrupt_callback(gpio_id=in_port,callback=SetLed, edge='rising', pull_up_down=RPIO.PUD_OFF, debounce_timeout_ms=25)
 
# Non Blocking wait (own Thread will do the interrupt handling)
RPIO.wait_for_interrupts(threaded=True)
 
#Do something others
while run:
	   # print "I'm in a loop and doing other things"
	   time.sleep(10)
 
# Clean up
RPIO.cleanup()

Und schon blinkt es so vor sich hin, die 59 Sekunde bleibt allerdings vorerst noch dunkel.

Setze man nun den Interrupt auf Steigende und fallende Flanke sieht mach an den Ausgaben schön da wir LOW und HIGH gut erkennen können. Damit lässt sich ein deutlich einfacheres und genaueres Programm erstellen.

Ablauf:

Methode 1 wird bei steigender Flanke gerufen
  HIGH Variable auf 0
  Messen wie viel Zeit seit letzten Aufruf von Methode 2 (fallende Flanke vergangen ist)
   => Falls > 1.7s (200ms max von 58Secounde + 800ms von 58s + 1000ms von 59 Sekunde ~ 1,7s mit Sicherheitszuschlag .-)) 
     => Start Minute gefunden  
        => Liste mit den Bitwerten um kopieren in die Ergebnis Liste
        => Liste mit den Bitwerten leeren
        => Bei Bedarf Systemzeit setzen        
Methode 2 wird bei fallender Flanke gerufen
  Über HIGH Variable messen wie viel Zeit vergangen ist
     => Falls > 190ms => Zur Liste mit dem Bit Werten eine 1 hinzufügen
     => Falls < 190ms => Zur Liste mit dem Bit Werten eine 0 hinzufügen
Endloss Schleife
    Prüfen ob alles geklappt hat, Parity Bits etc
    Anzeige der Ergebnisse über die Ergebnis Liste
    Ergebnis Liste in den Log File schreiben

Mehr dann nach dem nächste Tatort .-)


Erweiterungen

Weitere Erweiterungsmöglichkeiten

Oberfläche in der Console

Im nächsten Schritt kann das ganze dann mit zum Beispiel npyscreen mit einer hübschen Oberfläche optimiert werden.

Installation von npyscreen

yum install python-pip
python -m pip search npyscreen
python -m pip install  npyscreen

Mehr dazu:

Anzeige der Uhrzeit

Anzeige der Uhrzeit auf einer 7 Segment Anzeige über Shift Register



Anschluss über Serial Interface

Quellen

Cookies helfen bei der Bereitstellung von Inhalten. Durch die Nutzung dieser Seiten erklären Sie sich damit einverstanden, dass Cookies auf Ihrem Rechner gespeichert werden. Weitere Information
"Autor: Gunther Pipperr"
raspberry/dcf77_modul.txt · Zuletzt geändert: 2016/01/06 15:28 von Gunther Pippèrr