DS3231+DCF77 = Freeze

Hi, So i making clock based on DS3231 module able to adjust time with DCF77 signal. Everything seems to work like it should. BUT. When i try to adjust RTC by decoded DCF77 signal all hangs up. I'm strugling to find whats wrong in the code. Looking for assist.
P.S. If i use RTC based on millis (RTC_Millis rtc;) everething works as intend.

#include <util/parity.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "RTClib.h"

int interruptPin = 2;
int ledPin = 7;  // LED to monitor DCF77 signal

volatile unsigned long lastInt = 0;
volatile unsigned long long currentBuf = 0;
volatile byte bufCounter;

RTC_DS3231 rtc;
DateTime lastSyncTime;  // Variable to store the last sync time

// LCD I2C address
LiquidCrystal_I2C lcd(0x27, 20, 4); 

bool timeSynced = false;  // Flag to track if the time is synced

void setup() {
  // Start I2C communication
  Wire.begin();

  // Initialize the DS3231 RTC
  if (!rtc.begin()) {
    lcd.setCursor(0, 0);
    lcd.print("Couldn't find RTC");
    while (1);
  }

  if (rtc.lostPower()) {
    lcd.setCursor(0, 1);
    lcd.print("RTC lost power");
    // Set the RTC to the current date and time
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

  pinMode(interruptPin, INPUT);
  pinMode(ledPin, OUTPUT);  // Set LED pin as output
  digitalWrite(ledPin, LOW); // Turn off LED initially

  // Initialize the LCD
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.clear();

  // Set up interrupt for DCF77 signal
  attachInterrupt(digitalPinToInterrupt(interruptPin), DCF77_ISR, CHANGE);
}

void loop() {
  DateTime now = rtc.now(); 
  
  // Adjust for UTC +2
  now = DateTime(now.year(), now.month(), now.day(), now.hour() + 1, now.minute(), now.second());
  
  // Check if DST is active
  bool dstActive = isDST(now);

  // Adjust the time if DST is active (e.g., add 1 hour)
  if (dstActive) {
    now = DateTime(now.year(), now.month(), now.day(), now.hour() +1, now.minute(), now.second());
  }

  // Update the LCD display based on time sync status
    lcd.setCursor(0, 0);
  if (timeSynced) {
    lcd.print("Time Synced");
  } else {
    lcd.print("Time Not Synced");
  }

  // Print date (Day-Month-Year) on the first row
  lcd.setCursor(0, 1);
  lcd.print("Date: ");
  if (now.day() < 10) lcd.print('0');  // Add leading zero for day
  lcd.print(now.day());
  lcd.print('-');
  if (now.month() < 10) lcd.print('0');  // Add leading zero for month
  lcd.print(now.month());
  lcd.print('-');
  lcd.print(now.year());

  // Print time (Hour:Minute:Second) on the second row
  lcd.setCursor(0, 2);
  lcd.print("Time: ");
  if (now.hour() < 10) lcd.print('0');  // Add leading zero for hour
  lcd.print(now.hour());
  lcd.print(':');
  if (now.minute() < 10) lcd.print('0');  // Add leading zero for minute
  lcd.print(now.minute());
  lcd.print(':');
  if (now.second() < 10) lcd.print('0');  // Add leading zero for second
  lcd.print(now.second());

  // Print the day of the week on the third row
  lcd.setCursor(0, 3);
  lcd.print("Day: ");
  String dayOfWeek = dayOfWeekToString(now.dayOfTheWeek());
  lcd.print(dayOfWeek);

  delay(1000);  // Update every 1 second
}

// Interrupt service routine for DCF77 signal
void DCF77_ISR() {
  unsigned int dur = 0;
  dur = millis() - lastInt; 
  
  if (digitalRead(interruptPin)) {
    digitalWrite(ledPin, HIGH);  // Turn on LED when signal is HIGH
    if (dur > 1500) {
      if (bufCounter == 59) {
        evaluateSequence();
      }
      bufCounter = 0;
      currentBuf = 0;
    }
  } else {
    digitalWrite(ledPin, LOW);  // Turn off LED when signal is LOW
    if (dur > 150) {
      currentBuf |= ((unsigned long long)1 << bufCounter);
    }
    bufCounter++;
  }
  lastInt = millis();
}

// Evaluate the received DCF77 signal
void evaluateSequence() {
  byte dcf77Year = (currentBuf >> 50) & 0xFF;    // year = bit 50-57
  byte dcf77Month = (currentBuf >> 45) & 0x1F;   // month = bit 45-49
  byte dcf77DayOfWeek = (currentBuf >> 42) & 0x07;   // day of the week = bit 42-44
  byte dcf77DayOfMonth = (currentBuf >> 36) & 0x3F;  // day of the month = bit 36-41
  byte dcf77Hour = (currentBuf >> 29) & 0x3F;    // hour = bit 29-34
  byte dcf77Minute = (currentBuf >> 21) & 0x7F;  // minute = 21-27 
  bool parityBitMinute = (currentBuf >> 28) & 1;
  bool parityBitHour = (currentBuf >> 35) & 1;
  bool parityBitDate = (currentBuf >> 58) & 1;

  if ((parity_even_bit(dcf77Minute)) == parityBitMinute) {
    if ((parity_even_bit(dcf77Hour)) == parityBitHour) {
      if (((parity_even_bit(dcf77DayOfMonth) + parity_even_bit(dcf77DayOfWeek) 
           + parity_even_bit(dcf77Month) + parity_even_bit(dcf77Year)) % 2) == parityBitDate) {
        rtc.adjust(DateTime(rawByteToInt(dcf77Year) + 2000, rawByteToInt(dcf77Month), 
                            rawByteToInt(dcf77DayOfMonth), rawByteToInt(dcf77Hour), rawByteToInt(dcf77Minute), 0));
        timeSynced = true;  // Mark time as synced
      }
    }
  }
}

// Convert raw DCF77 byte to integer
unsigned int rawByteToInt(byte raw) {
  return ((raw >> 4) * 10 + (raw & 0x0F));
}

// Convert day of the week number to string
String dayOfWeekToString(int day) {
  switch (day) {
    case 0: return "Sun";
    case 1: return "Mon";
    case 2: return "Tue";
    case 3: return "Wed";
    case 4: return "Thu";
    case 5: return "Fri";
    case 6: return "Sat";
    default: return "Invalid";
  }
}

// Check if DST is active 
bool isDST(DateTime now) {
  int month = now.month();
  int day = now.day();
  
  if (month < 3 || month > 10) {  // Outside the DST months (March-October)
    return false;
  }

  // Check for the last Sunday of March
  if (month == 3) {
    int weekday = now.dayOfTheWeek();
    if (day >= 25 && weekday == 0) {  // If it's the last Sunday of March
      return true;
    }
  }

  // Check for the last Sunday of October
  if (month == 10) {
    int weekday = now.dayOfTheWeek();
    if (day >= 25 && weekday == 0) {  // If it's the last Sunday of October
      return false;
    }
  }

  return true;  // Between the last Sunday of March and the last Sunday of October, DST is active
}

Welcome to the forums. +1 for putting your code in code-tags.

Have you tried the DCF77 library?

You can install it from the library manager. Not sure how different it is from your code. It does mention that it may take up to 2 minutes to first sync.

What does "all hangs up" mean, precisely? What exactly do you observe?

By "hangs up" i mean it literally get froze (display update stops, time stops, LED that indicate signal not pulsing, etc) board need to be reseted.

Ah. Well, calling rtc.adjust from an interrupt context might be something to rethink. Possibly just set a flag to indicate that you've got good data from the DCF77 and then adjust the time in the main thread.

pinMode(interruptPin, INPUT);

attachInterrupt(digitalPinToInterrupt(interruptPin), DCF77_ISR, CHANGE);
}

You have rather a lot going on in your void DCF77_ISR(){} routine. I wonder whether the input, being high Z is picking up noise and triggering random state changes which are then swamping the MCU?

The quiet state of the DCF77 is low and the pulses are high. I wonder whether it might be worth adding a pull-down resistor?.

ISRs should generally be as short as possible, perhaps even only stetting a variable to indicate that the interrupt has been triggered or its state changed. The bulk of the work should really be done in the main code.

If you are going to update timeSynced within the ISR function (it is being called from void DFC77_ISR) then the variable should probably be declared as volatile. However I think that the work performed by that function should probably be done within the main program rather than the ISR call.