<===

ProNotes

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)
← Previous Next →
Back to list