Files

124 lines
4.9 KiB
Python

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"