#!/usr/bin/python3 """ Script to set the RTC using an NTP server. RPI needs to be connected to the internet (WIFI). Requires ntplib and pytz: sudo pip3 install ntplib pytz --break-system-packages """ import smbus2 import time from datetime import datetime import ntplib import pytz # For timezone handling # DS3231 I2C address DS3231_ADDR = 0x68 # Registers for DS3231 REG_TIME = 0x00 def bcd_to_dec(bcd): """Convert BCD to decimal.""" return (bcd // 16 * 10) + (bcd % 16) def dec_to_bcd(dec): """Convert decimal to BCD.""" return (dec // 10 * 16) + (dec % 10) def set_time(bus, year, month, day, hour, minute, second): """Set the RTC time.""" # Convert the time to BCD format second_bcd = dec_to_bcd(second) minute_bcd = dec_to_bcd(minute) hour_bcd = dec_to_bcd(hour) day_bcd = dec_to_bcd(day) month_bcd = dec_to_bcd(month) year_bcd = dec_to_bcd(year - 2000) # DS3231 uses year from 2000 # Write time to DS3231 bus.write_i2c_block_data(DS3231_ADDR, REG_TIME, [ second_bcd, minute_bcd, hour_bcd, 0x01, # Day of the week (1=Monday, etc.) day_bcd, month_bcd, year_bcd ]) def read_time(bus): """Read the RTC time and validate the values.""" try: data = bus.read_i2c_block_data(DS3231_ADDR, REG_TIME, 7) # Convert from BCD second = bcd_to_dec(data[0] & 0x7F) minute = bcd_to_dec(data[1]) hour = bcd_to_dec(data[2] & 0x3F) day = bcd_to_dec(data[4]) month = bcd_to_dec(data[5]) year = bcd_to_dec(data[6]) + 2000 # Print raw values for debugging print(f"Raw RTC values: {data}") print(f"Decoded values: Y:{year} M:{month} D:{day} H:{hour} M:{minute} S:{second}") # Validate date values if not (1 <= month <= 12): print(f"Invalid month value: {month}, using default") month = 1 # Check days in month (simplified) days_in_month = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] if not (1 <= day <= days_in_month[month]): print(f"Invalid day value: {day} for month {month}, using default") day = 1 # Validate time values if not (0 <= hour <= 23): print(f"Invalid hour value: {hour}, using default") hour = 0 if not (0 <= minute <= 59): print(f"Invalid minute value: {minute}, using default") minute = 0 if not (0 <= second <= 59): print(f"Invalid second value: {second}, using default") second = 0 return (year, month, day, hour, minute, second) except Exception as e: print(f"Error reading RTC: {e}") # Return a safe default date (2023-01-01 00:00:00) return (2023, 1, 1, 0, 0, 0) def get_internet_time(): """Get the current time from an NTP server.""" ntp_client = ntplib.NTPClient() # Try multiple NTP servers in case one fails servers = ['pool.ntp.org', 'time.google.com', 'time.windows.com', 'time.apple.com'] for server in servers: try: print(f"Trying NTP server: {server}") response = ntp_client.request(server, timeout=2) utc_time = datetime.utcfromtimestamp(response.tx_time) print(f"Successfully got time from {server}") return utc_time except Exception as e: print(f"Failed to get time from {server}: {e}") # If all servers fail, raise exception raise Exception("All NTP servers failed") def main(): try: bus = smbus2.SMBus(1) # Test if RTC is accessible try: bus.read_byte(DS3231_ADDR) print("RTC module is accessible") except Exception as e: print(f"Error accessing RTC module: {e}") print("Please check connections and I2C configuration") return # Get the current time from the RTC try: year, month, day, hours, minutes, seconds = read_time(bus) # Create datetime object with validation to handle invalid dates rtc_time = datetime(year, month, day, hours, minutes, seconds) print(f"Actual RTC Time : {rtc_time.strftime('%Y-%m-%d %H:%M:%S')}") except ValueError as e: print(f"Invalid date/time read from RTC: {e}") print("Will proceed with setting RTC from internet time") rtc_time = None # Get current UTC time from an NTP server try: internet_utc_time = get_internet_time() print(f"Time from Internet (UTC) : {internet_utc_time.strftime('%Y-%m-%d %H:%M:%S')}") except Exception as e: print(f"Error retrieving time from the internet: {e}") if rtc_time is None: print("Cannot proceed without either valid RTC time or internet time") return print("Will keep current RTC time") return # Set the RTC to UTC time print("Setting RTC to internet time...") set_time(bus, internet_utc_time.year, internet_utc_time.month, internet_utc_time.day, internet_utc_time.hour, internet_utc_time.minute, internet_utc_time.second) # Read and print the new time from RTC print("Reading back new RTC time...") year, month, day, hour, minute, second = read_time(bus) rtc_time_new = datetime(year, month, day, hour, minute, second) print(f"New RTC Time (UTC) : {rtc_time_new.strftime('%Y-%m-%d %H:%M:%S')}") # Calculate difference to verify accuracy time_diff = abs((rtc_time_new - internet_utc_time).total_seconds()) print(f"Time difference : {time_diff:.2f} seconds") if time_diff > 5: print("Warning: RTC time differs significantly from internet time") print("You may need to retry or check RTC module") else: print("RTC successfully synchronized with internet time") except Exception as e: print(f"Unexpected error: {e}") if __name__ == "__main__": main()