Files
nebuleair_pro_4g/RTC/set_with_NTP.py
Your Name 10c4348e54 update
2025-03-10 13:38:02 +01:00

183 lines
6.3 KiB
Python
Executable File

#!/usr/bin/python3
"""
____ _____ ____
| _ \_ _/ ___|
| |_) || || |
| _ < | || |___
|_| \_\|_| \____|
Script to set the RTC using an NTP server (script used by web UI)
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()