- Получение времени из интернета: Запрашивает текущее время с веб-сайта (
http://ur4uqu.com/time/kyiv.php`) для синхронизации внутреннего таймера. - Вывод времени: Выводит текущую дату и время в формате
ГГГГ-ММ-ДД\nЧЧ:ММ:ССкаждую секунду на стандартный вывод (stdout) через последовательный порт (например, в терминале). - Периодическая синхронизация: Каждые 5 (xx) минут пытается повторно получить время с сайта для корректировки внутреннего таймера, чтобы компенсировать возможный дрейф времени.
- Устойчивость к сбоям: Обеспечивает непрерывную работу даже при временной потере Wi-Fi-соединения, используя внутренний таймер, и повторяет попытки синхронизации.
Проект полезен для приложений, требующих точного отслеживания времени, например, для часов, логгеров данных или IoT-устройств, работающих в условиях нестабильного интернет-соединения.
Описание проекта
Проект представляет собой скрипт на языке CircuitPython, который выполняет следующие задачи:
1. Подключение к Wi-Fi:
- Использует модуль
wifiдля подключения к сети Wi-Fi с заданными учетными данными (SSID и пароль). - Реализована функция
connect_wifiс механизмом повторных попыток (до 3 раз с задержкой 5 секунд) для обработки временных сбоев сети. - После каждой попытки синхронизации Wi-Fi отключается для экономии энергии с помощью функции
disconnect_wifi.
2. Получение и обработка времени:
- Скрипт запрашивает время с веб-сайта, который возвращает данные в формате
UTC: ГГГГ-ММ-ДД ЧЧ:ММ:СС. - Функция
parse_timeразбирает полученную строку, извлекая год, месяц, день, часы, минуты и секунды. - Время преобразуется в секунды с начала эпохи Unix (
time.mktime) для внутреннего отслеживания.
3. Внутренний таймер:
- Поскольку ESP32-C3 не имеет аппаратного RTC (реального времени часов), скрипт использует
time.monotonic()для отслеживания времени между синхронизациями. - Если начальная синхронизация не удалась, используется резервное время (
2025-01-01 00:00:00), чтобы избежать ошибок переполнения вtime.localtime().
4. Вывод времени:
- Каждую секунду скрипт форматирует текущее время в виде строки
ГГГГ-ММ-ДД\nЧЧ:ММ:ССи выводит её черезprint. - Время вычисляется путем добавления прошедших секунд (на основе
time.monotonic()) к последнему синхронизированному времени.
5. Периодическая синхронизация:
- Каждые 5 минут (300 секунд) скрипт пытается повторно подключиться к Wi-Fi, получить время с сайта и обновить внутренний таймер.
- Если синхронизация не удалась (например, из-за отсутствия Wi-Fi или недоступности сайта), скрипт продолжает использовать внутренний таймер и повторяет попытку через 5 минут.
6. Обработка ошибок:
- Обрабатываются ошибки подключения к Wi-Fi, запросов HTTP и парсинга времени.
- При сбое начальной синхронизации используется резервное время, чтобы избежать сбоев программы (например,
OverflowErrorиз-за некорректного времени). - Wi-Fi отключается после каждой попытки синхронизации, чтобы избежать проблем с устаревшими соединениями.
Технические особенности
- Платформа: 01Space 0.42 OLED ESP32C3 с прошивкой CircuitPython 9.2.9.
- Модули: Используются
wifi,socketpool,ssl,adafruit_requestsдля работы с сетью и HTTP-запросами, а такжеtimeдля управления временем. - Ограничения: Отсутствие аппаратного RTC требует программного отслеживания времени, что может привести к небольшому дрейфу, корректируемому периодической синхронизацией.
- Энергосбережение: Wi-Fi отключается между синхронизациями для снижения энергопотребления.
Пример вывода
При успешной работе скрипт выводит:
Connecting to Wi-Fi...
Connected to Wi-Fi
Fetched time: UTC: 2025-10-09 13:32:00
2025-10-09
13:32:00
2025-10-09
13:32:01
Через xx минут:
Attempting to sync time...
Connecting to Wi-Fi...
Connected to Wi-Fi
Fetched time: UTC: 2025-10-09 13:37:00
Disconnected from Wi-Fi
Возможные улучшения
- Вывод на OLED-дисплей: Интеграция с OLED-дисплеем (например, с использованием библиотеки
adafruit_ssd1306) для отображения времени. - Коррекция часового пояса: Добавление смещения времени для локального часового пояса (например, UTC+2/UTC+3 для Киева).
- Логирование: Сохранение ошибок или истории синхронизаций в файл на файловой системе микроконтроллера.
- Уведомления: Добавление индикации (например, через светодиод) при успешной/неуспешной синхронизации.
==========================================
$ cat code.py
import time
import wifi
import socketpool
import ssl
import adafruit_requests
Wi-Fi credentials
SSID = "F_2"
PASSWORD = "*****"
URL for fetching time
URL = "http://ur4uqu.com/time/kyiv.php"
Function to connect to Wi-Fi with retries
def connect_wifi(max_retries=3, retry_delay=5):
if wifi.radio.connected:
return True
print("Connecting to Wi-Fi...")
for attempt in range(max_retries):
try:
wifi.radio.connect(SSID, PASSWORD)
print("Connected to Wi-Fi")
return True
except Exception as e:
print(f"Wi-Fi connection failed (attempt {attempt + 1}/{max_retries}):", str(e))
if attempt < max_retries - 1:
time.sleep(retry_delay)
return False
Function to disconnect from Wi-Fi
def disconnect_wifi():
if wifi.radio.connected:
wifi.radio.stop_station()
print("Disconnected from Wi-Fi")
Function to parse time from the website response (format: "UTC: YYYY-MM-DD HH:MM:SS")
def parse_time(time_str):
try:
Extract the date and time part after "UTC: "
time_str = time_str.split("UTC: ")[1]
date_part, time_part = time_str.split(" ")
year, month, day = map(int, date_part.split("-"))
hour, minute, second = map(int, time_part.split(":"))
return year, month, day, hour, minute, second
except Exception as e:
print("Error parsing time:", str(e))
return None
Function to fetch time from the website and return as seconds since epoch
def sync_time():
if not connect_wifi():
return None, None
Initialize HTTP requests after connection
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())
try:
response = requests.get(URL)
time_data = response.text.strip()
print("Fetched time:", time_data)
parsed_time = parse_time(time_data)
if parsed_time:
year, month, day, hour, minute, second = parsed_time
Create a time struct and convert to seconds since epoch
time_struct = time.struct_time((year, month, day, hour, minute, second, 0, -1, -1))
return time.mktime(time_struct), time.monotonic()
else:
print("Failed to parse time")
return None, None
except Exception as e:
print("Error fetching time:", str(e))
return None, None
finally:
disconnect_wifi()
Initial time synchronization
epoch_time, last_sync_monotonic = sync_time()
if epoch_time is None:
print("Initial time sync failed, using fallback time (2025-01-01 00:00:00), retrying in 5 minutes...")
Use a fallback timestamp (2025-01-01 00:00:00) to avoid OverflowError
fallback_time = time.struct_time((2025, 1, 1, 0, 0, 0, 2, 1, -1))
epoch_time = time.mktime(fallback_time)
last_sync_monotonic = time.monotonic()
Main loop
last_sync = time.monotonic() # Track time of last sync attempt
sync_interval = 60 * 60 # xx minutes in seconds
while True:
Calculate current time by adding elapsed seconds since last sync
elapsed_seconds = int(time.monotonic() - last_sync_monotonic)
current_epoch_time = epoch_time + elapsed_seconds
current_time = time.localtime(current_epoch_time)
Format date and time for printing
date_str = f"{current_time.tm_year}-{current_time.tm_mon:02d}-{current_time.tm_mday:02d}"
time_str = f"{current_time.tm_hour:02d}:{current_time.tm_min:02d}:{current_time.tm_sec:02d}"
Print date and time every second
print(f"{date_str} {time_str}")
Check if xx minutes have passed since last sync
if time.monotonic() - last_sync >= sync_interval:
print("Attempting to sync time...")
new_epoch_time, new_monotonic = sync_time()
if new_epoch_time is not None:
epoch_time = new_epoch_time
last_sync_monotonic = new_monotonic
last_sync = time.monotonic() # Update last sync time
Wait 1 second before next print
time.sleep(1)
[file-name 000254_2025-10-09_11-00-10.txt]