import time from machine import I2C, Pin, RTC class PCF85063: """Utility class for the PCF85063 Real-Time Clock (RTC) chip.""" ADDR = 0x51 # Starting register for timekeeping data TIME_REG_START = 0x04 def __init__(self, i2c=None): if i2c is None: # Default to ESP32-S3-RLCD-4.2 onboard I2C pins from machine import SoftI2C self.i2c = SoftI2C(sda=Pin(13), scl=Pin(14)) else: self.i2c = i2c # Initialize RTC if needed (ensure oscillator is running) self._init_rtc() def _init_rtc(self): try: # Control register 1 (0x00) should be 0 to ensure oscillator is not stopped (bit 5 = STOP) ctrl1 = self.i2c.readfrom_mem(self.ADDR, 0x00, 1)[0] if ctrl1 & 0x20: # Oscillator is stopped print("RTC oscillator was stopped. Starting oscillator...") self.i2c.writeto_mem(self.ADDR, 0x00, b'\x00') except Exception as e: print(f"Error initializing PCF85063: {e}") def _dec2bcd(self, val): return (val // 10 << 4) | (val % 10) def _bcd2dec(self, val): return ((val >> 4) * 10) + (val & 0x0F) def get_datetime(self): """Reads current time from hardware RTC. Returns: tuple: (year, month, day, weekday, hour, minute, second) or None on error. Note: year is 4-digit (e.g. 2026). """ try: # Read 7 registers: Seconds, Minutes, Hours, Days, Weekdays, Months, Years data = self.i2c.readfrom_mem(self.ADDR, self.TIME_REG_START, 7) second = self._bcd2dec(data[0] & 0x7F) # Bit 7 is integrity flag OS minute = self._bcd2dec(data[1] & 0x7F) hour = self._bcd2dec(data[2] & 0x3F) # 24-hour mode day = self._bcd2dec(data[3] & 0x3F) weekday = data[4] & 0x07 # 0-6 month = self._bcd2dec(data[5] & 0x1F) year = 2000 + self._bcd2dec(data[6]) # 0-99 mapping to 2000-2099 return (year, month, day, weekday, hour, minute, second) except Exception as e: print(f"Error reading PCF85063 RTC: {e}") return None def set_datetime(self, dt): """Sets the hardware RTC time. Args: dt (tuple): (year, month, day, weekday, hour, minute, second) Note: year can be 4-digit (e.g. 2026) or 2-digit (e.g. 26). """ try: year, month, day, weekday, hour, minute, second = dt # Format to registers reg_year = year % 100 data = bytearray(7) data[0] = self._dec2bcd(second) & 0x7F # Clear OS bit (re-starts oscillator integrity check) data[1] = self._dec2bcd(minute) data[2] = self._dec2bcd(hour) data[3] = self._dec2bcd(day) data[4] = weekday & 0x07 data[5] = self._dec2bcd(month) data[6] = self._dec2bcd(reg_year) self.i2c.writeto_mem(self.ADDR, self.TIME_REG_START, data) return True except Exception as e: print(f"Error setting PCF85063 RTC: {e}") return False def sync_to_system(self): """Synchronizes the ESP32-S3 internal system time from this hardware RTC.""" dt = self.get_datetime() if dt: year, month, day, weekday, hour, minute, second = dt # machine.RTC.datetime requires 8-tuple: (year, month, day, weekday, hour, minute, second, subseconds) sys_rtc = RTC() sys_rtc.datetime((year, month, day, weekday, hour, minute, second, 0)) print(f"System clock synced to RTC: {year:04d}-{month:02d}-{day:02d} {hour:02d}:{minute:02d}:{second:02d}") return True return False def sync_from_system(self): """Synchronizes the hardware RTC time from the ESP32-S3 system clock.""" try: # Get current system time (localtime tuple: year, month, mday, hour, minute, second, weekday, yearday) t = time.localtime() # mapping time.localtime() weekday to PCF85063: # Python weekday (0=Monday...6=Sunday) dt = (t[0], t[1], t[2], t[6], t[3], t[4], t[5]) success = self.set_datetime(dt) if success: print(f"RTC synced from System: {t[0]:04d}-{t[1]:02d}-{t[2]:02d} {t[3]:02d}:{t[4]:02d}:{t[5]:02d}") return success except Exception as e: print(f"Error syncing RTC from system: {e}") return False def get_time_string(self): """Returns current time as a formatted string: YYYY-MM-DD HH:MM:SS""" dt = self.get_datetime() if dt: return f"{dt[0]:04d}-{dt[1]:02d}-{dt[2]:02d} {dt[4]:02d}:{dt[5]:02d}:{dt[6]:02d}" return "0000-00-00 00:00:00"