• Получение времени из интернета: Запрашивает текущее время с веб-сайта (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]