from machine import Pin
import micropython
import time
micropython.alloc_emergency_exception_buf(100) # emergency exception buffer
BIT_TIME = 140 # this value should be between 100 and 200, it decides between 0 and 1
MINUTE_UP = 1400 # the minute is up
VALUES = [1, 2, 4, 8, 10, 20, 40, 80]
WEEKDAYS = ["--", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]
CEST = ["CET", "CEST"]
ledPIN = Pin(6, Pin.OUT) # onboard LED
dcfPIN = Pin(26, Pin.IN) # DCF77 signal input
ticks = 0 # global variables
cest = 0
minute = 0
hour = 0
day = 0
weekday = 0
month = 0
year = 0
position = 0
parity = 0 # even parity in the time signal
def print_dcf(arg): # Interrupts do not like serial communication, so a schedule for
print(arg) # outputs is created in the interrupt and processed here, all outputs
# in the interrupt are added to the schedule
def callback_dcf77(channel): # this function responds to high and low changes in the input
global ticks, cest, minute, hour, day, weekday, month, year, position, parity
global VALUES, WEEKDAYS, CEST, BIT_TIME, MINUTE_UP
ledPIN.value(dcfPIN.value()) # live update for the onboard LED
_ticks = time.ticks_ms() # increasing millisecond counter
len = time.ticks_diff(_ticks, ticks) # calculates the pulse length
ticks = _ticks # remember the value
if dcfPIN.value() == 0: # this means that the signal was high and is now low
# micropython.schedule(print_dcf, len) # enable this line to see the bit length
if len > BIT_TIME: # zero or one ?
parity += 1 # one !
if 21 <= position <= 27: # minute
minute += VALUES[position - 21]
elif 29 <= position <= 34: # hour
hour += VALUES[position - 29]
elif 36 <= position <= 41: # day
day += VALUES[position - 36]
elif 42 <= position <= 44: # weekday
weekday += VALUES[position - 42]
elif 45 <= position <= 49: # month
month += VALUES[position - 45]
elif 50 <= position <= 57: # year
year += VALUES[position - 50] # only the year, not the century
if position == 17:
cest = 1 # Central European Summertime
elif position == 18:
if cest == 1: # Central European Time
position = 0 # Error: sets the position to 0, making the time information invalid
elif position == 20: # start of encoded time, always 1
parity = 0 # reset parity for the following bits
elif position == 28 or position == 35 or position == 58:
if (parity % 2) != 0: # even parity ?
position = 0 # Error: sets the position to 0, making the time information invalid
parity = 0 # reset parity for the following bits
else: # zero
if position == 28 or position == 35 or position == 58:
if (parity % 2) != 0: # even parity ?
position = 0 # Error: sets the position to 0, making the time information invalid
parity = 0 # reset parity for the following bits
micropython.schedule(print_dcf, 'Bit: %d' % position) # optional
# micropython.schedule(print_dcf, "Timestamp: %d-%02d-%02d %02d:%02d %s %s" %
# ((year + 2000), month, day, hour, minute,
# WEEKDAYS[weekday], CEST[cest]))
# enable the lines above to see how the date and time evolve
position += 1 # increment the bit counter
else:
if len > MINUTE_UP:
# the minute is up, the parity bit does not allow error correction,
# so some additional checks are performed
if position == 59 and (0 <= minute <= 59 or 0 <= hour <= 23 or
1 <= day <= 31 or 1 <= weekday <= 7 or
1 <= month <= 12 or year >= 25):
micropython.schedule(print_dcf, "Timestamp: %d-%02d-%02d %02d:%02d %s %s" %
((year + 2000), month, day, hour, minute,
WEEKDAYS[weekday], CEST[cest]))
else:
micropython.schedule(print_dcf, "No valid timestamp")
position = 0 # reset everything, preparing for the next minute
cest = 0
minute = 0
hour = 0
day = 0
weekday = 0
month = 0
year = 0
parity = 0
# end of 'callback_dcf77'
# the program starts here
print("Start...DCF77") # 'Hello world'
# define callback function and trigger
dcfPIN.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=callback_dcf77)
while True: # endless loop
time.sleep(0.2)