Last active
February 28, 2026 20:55
-
-
Save Lukelectro/24a3847f5a7d357eb5bd060d50728bc2 to your computer and use it in GitHub Desktop.
DCF-77 clock on 50228 display
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //select atmega161 from majorcore, at 8Mhz external oscilator. DCF-77 receiver is connected to PE0, arduino pin 32, INT2. | |
| #include <dcf77.h> // Udo Klein Blinkenlight DCF77 crystal library | |
| #include <MSL50228.h> //for the displays | |
| const uint8_t dcf77_sample_pin = 32; // 32 PE0 INT2 | |
| const uint8_t dcf77_inverted_samples = 1; | |
| const uint8_t dcf77_pin_mode = INPUT_PULLUP; // enable internal pull up | |
| const uint8_t dcf77_monitor_led = -1; // Not used | |
| MSL50228 display; | |
| uint8_t ledpin(const uint8_t led) { | |
| return led; | |
| } | |
| uint8_t sample_input_pin() { | |
| const uint8_t sampled_data = | |
| dcf77_inverted_samples ^ digitalRead(dcf77_sample_pin); | |
| digitalWrite(ledpin(dcf77_monitor_led), sampled_data); | |
| return sampled_data; | |
| } | |
| void setup() { | |
| using namespace Clock; | |
| Serial.begin(115200); | |
| sprintln(); | |
| sprintln(F("Simple DCF77 Clock V3.1.1")); | |
| sprintln(F("(c) Udo Klein 2016")); | |
| sprintln(F("www.blinkenlight.net")); | |
| sprintln(); | |
| sprint(F("Sample Pin: ")); | |
| sprintln(dcf77_sample_pin); | |
| sprint(F("Sample Pin Mode: ")); | |
| sprintln(dcf77_pin_mode); | |
| sprint(F("Inverted Mode: ")); | |
| sprintln(dcf77_inverted_samples); | |
| sprint(F("Monitor Pin: ")); | |
| sprintln(ledpin(dcf77_monitor_led)); | |
| sprintln(); | |
| sprintln(); | |
| sprintln(F("Initializing...")); | |
| pinMode(ledpin(dcf77_monitor_led), OUTPUT); | |
| pinMode(dcf77_sample_pin, dcf77_pin_mode); | |
| DCF77_Clock::setup(); | |
| DCF77_Clock::set_input_provider(sample_input_pin); | |
| display.write("Waitfor sync"); | |
| // Wait till clock is synced, depending on the signal quality this may take | |
| // rather long. About 5 minutes with a good signal, 30 minutes or longer | |
| // with a bad signal | |
| for (uint8_t state = Clock::useless; | |
| state == Clock::useless || state == Clock::dirty; | |
| state = DCF77_Clock::get_clock_state()) { | |
| // wait for next sec | |
| Clock::time_t now; | |
| DCF77_Clock::get_current_time(now); | |
| // render one dot per second while initializing | |
| static uint8_t count = 0; | |
| sprint('.'); | |
| ++count; | |
| if (count == 60) { | |
| count = 0; | |
| sprintln(); | |
| } | |
| if (count % 2) { | |
| display.writeAt(5, "+"); | |
| } else { | |
| display.writeAt(5, "*"); | |
| } | |
| } | |
| display.writeAt(5, " "); // clear the dot. | |
| } | |
| void paddedPrint(BCD::bcd_t n) { | |
| sprint(n.digit.hi); | |
| sprint(n.digit.lo); | |
| } | |
| void loop() { | |
| Clock::time_t now; | |
| char disps[6][4]; | |
| static bool onOFF = false; | |
| DCF77_Clock::get_current_time(now); | |
| if (now.month.val > 0) { | |
| switch (DCF77_Clock::get_clock_state()) { // only print seconds when they are reliable (Clock locked or even synced) | |
| case Clock::useless: | |
| default: | |
| sprint(F("useless ")); | |
| disps[4][0] = 'F'; | |
| disps[4][1] = 'O'; | |
| disps[4][2] = 'U'; | |
| disps[4][3] = 'T'; | |
| //when clock is useless, print "FOUT" (Meaning "WRONG" but convieniently 4 letters so it fits the display ;)) | |
| break; | |
| case Clock::dirty: | |
| sprint(F("dirty: ")); | |
| disps[4][0] = ' '; | |
| disps[4][1] = ' '; | |
| disps[4][2] = '?'; | |
| disps[4][3] = '?'; | |
| break; | |
| case Clock::free: | |
| sprint(F("free: ")); | |
| disps[4][0] = '?'; | |
| disps[4][1] = '?'; | |
| disps[4][2] = now.second.digit.hi + '0'; | |
| disps[4][3] = now.second.digit.lo + '0'; | |
| break; | |
| case Clock::unlocked: | |
| sprint(F("unlocked: ")); | |
| disps[4][0] = ' '; | |
| disps[4][1] = '?'; | |
| disps[4][2] = now.second.digit.hi + '0'; | |
| disps[4][3] = now.second.digit.lo + '0'; | |
| break; | |
| case Clock::synced: | |
| sprint(F("synced: ")); | |
| disps[4][0] = ' '; | |
| disps[4][1] = ' '; | |
| disps[4][2] = now.second.digit.hi + '0'; | |
| disps[4][3] = now.second.digit.lo + '0'; | |
| break; | |
| case Clock::locked: | |
| sprint(F("locked: ")); | |
| disps[4][0] = ' '; | |
| disps[4][1] = ' '; | |
| disps[4][2] = now.second.digit.hi + '0'; | |
| disps[4][3] = now.second.digit.lo + '0'; | |
| break; | |
| } | |
| sprint(' '); | |
| sprint(F("20")); | |
| paddedPrint(now.year); | |
| sprint('-'); | |
| paddedPrint(now.month); | |
| sprint('-'); | |
| paddedPrint(now.day); | |
| sprint(' '); | |
| paddedPrint(now.hour); | |
| sprint(':'); | |
| paddedPrint(now.minute); | |
| sprint(':'); | |
| paddedPrint(now.second); | |
| sprint("+0"); | |
| sprint(now.uses_summertime ? '2' : '1'); | |
| sprintln(); | |
| disps[0][0] = now.hour.digit.hi + '0'; // +'0' converts number to ASCII ;) | |
| disps[0][1] = now.hour.digit.lo + '0'; | |
| disps[0][2] = ' '; | |
| disps[0][3] = ' '; | |
| disps[2][0] = ' '; | |
| disps[2][1] = now.minute.digit.hi + '0'; | |
| disps[2][2] = now.minute.digit.lo + '0'; | |
| disps[2][3] = ' '; | |
| disps[1][0] = now.day.digit.hi + '0'; | |
| disps[1][1] = now.day.digit.lo + '0'; | |
| disps[1][2] = ' '; | |
| disps[1][3] = ' '; | |
| if (now.month.digit.hi == 0) { | |
| switch (now.month.digit.lo) { | |
| case 1: | |
| disps[3][0] = 'J'; | |
| disps[3][1] = 'a'; | |
| disps[3][2] = 'n'; | |
| break; | |
| case 2: | |
| disps[3][0] = 'F'; | |
| disps[3][1] = 'e'; | |
| disps[3][2] = 'b'; | |
| break; | |
| case 3: | |
| disps[3][0] = 'M'; | |
| disps[3][1] = 'a'; | |
| disps[3][2] = 'r'; | |
| break; | |
| case 4: | |
| disps[3][0] = 'A'; | |
| disps[3][1] = 'p'; | |
| disps[3][2] = 'r'; | |
| break; | |
| case 5: | |
| disps[3][0] = 'M'; | |
| disps[3][1] = 'e'; | |
| disps[3][2] = 'i'; | |
| break; | |
| case 6: | |
| disps[3][0] = 'J'; | |
| disps[3][1] = 'u'; | |
| disps[3][2] = 'n'; | |
| break; | |
| case 7: | |
| disps[3][0] = 'J'; | |
| disps[3][1] = 'u'; | |
| disps[3][2] = 'l'; | |
| break; | |
| case 8: | |
| disps[3][0] = 'A'; | |
| disps[3][1] = 'u'; | |
| disps[3][2] = 'g'; | |
| break; | |
| case 9: | |
| disps[3][0] = 'S'; | |
| disps[3][1] = 'e'; | |
| disps[3][2] = 'p'; | |
| break; | |
| } | |
| } else { | |
| switch (now.month.digit.lo) { | |
| case 0: | |
| disps[3][0] = 'O'; | |
| disps[3][1] = 'k'; | |
| disps[3][2] = 't'; | |
| break; | |
| case 1: | |
| disps[3][0] = 'N'; | |
| disps[3][1] = 'o'; | |
| disps[3][2] = 'v'; | |
| break; | |
| case 2: | |
| disps[3][0] = 'D'; | |
| disps[3][1] = 'e'; | |
| disps[3][2] = 'c'; | |
| break; | |
| } | |
| } | |
| disps[3][3] = ' '; // set all unused ones to spaces (So the uninitialised content does not acidentaly blank a display) | |
| disps[5][0] = '2'; | |
| disps[5][1] = '0'; | |
| disps[5][2] = now.year.digit.hi + '0'; | |
| disps[5][3] = now.year.digit.lo + '0'; | |
| display.clear(); | |
| display.writeAt(0, disps[0]); | |
| display.writeAt(1, disps[1]); | |
| display.writeAt(2, disps[2]); | |
| display.writeAt(3, disps[3]); | |
| display.writeAt(4, disps[4]); | |
| display.writeAt(5, disps[5]); | |
| // display is quite hungry, so only on when button_down is pressed or at the start of the hour, or when it is switched on with button_up | |
| if ((digitalRead(B_DOWN) == 0) || now.minute.val == 0 || onOFF == true) { | |
| display.setBrightness(3); | |
| } else { | |
| display.setBrightness(0); | |
| } | |
| if (digitalRead(B_UP) == 0) { | |
| while (digitalRead(B_UP) == 0) delay(100); | |
| onOFF = !onOFF; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment