<===
2025-10-09 11:00:10
Проект предназначен для создания программного решения на микроконтроллере 01Space 0.42 OLED ESP32C3 с использованием CircuitPython 9.2.9, которое выполняет следующие функции:
- **Получение времени из интернета**: Запрашивает текущее время с веб-сайта (`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)